泛型是c#2.0中一个重要的新特性,泛型是clr和编程语言提供的一种特殊机制,它支持另一种形式的代码重用。泛型通常用与集合以及作用于集合的方法一起使用,当然也可以单独使用.
c#是一种强类型的语言,在泛型没有被提出之前,我们在使用集合的代码的时候,每次对其进行转换都需要隐式的强制转换,我们都知道所有对象的最终基类是object,我们在每次使用object的时候,无论是变换什么类型都要对其进行强制转换。
那么有了泛型之后,使用泛型我们就无需进行转换,因为泛型根据接收者传入的参数类型,直接将每个类型更改为所传入的参数类型.
一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。 创建您自己的泛型类时,需要特别注意以下事项:
将哪些类型通用化为类型参数。
通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。 但是,太多的通用化会使其他开发人员难以阅读或理解代码。
如果存在约束,应对类型参数应用什么约束
一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。 例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。 这可以防止您的类被意外地用于值类型,并允许您对 t 使用 as 运算符以及检查空值。
是否将泛型行为分解为基类和子类。
由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。 请参见本主题后面有关从泛型基类继承的规则。
是否实现一个或多个泛型接口。
例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如 icomparable,其中 t 是您的类的类型。
【2】:泛型的表示方式
泛型可以为引用类型和值类型还有接口和委托,fcl( dll程序集,包含了.net框架下的各种dll )类中定义了一个泛型列表,用来管理一个对象集合,如果我们想要使用这个泛型列表,可以在使用时指定具体数据类型。
泛型的表示为 “t” 如:list8742468051c85b06f0a0af9e3e506b5c, t 表示一个未指定的数据类型,我们可以看一下fcl类中泛型的引用定义:
system.collections.generic 命名空间包含定义泛型集合的接口和类,泛型集合允许用户创建强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能。
创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡
【3】:泛型的好处
1 : 使代码更加的简洁,清晰
从前面我们也提到了,泛型具备可重用性 , 减少我们代码量, 使我们的程序更容易开发和维护,举例 :
实例 1 : 在从数据库中获取数据库的时候,我们经常会返回一个datatable类型,然后将其转换为list集合. 那么如果没有泛型的话,我会一般会这样来做.
public list<traininguser>gettraininguser(string userid) { datatable dt = sqlitehelper.executedataset(system.data.commandtype.text, @" select distinct userid,trainingid from traininguser as tu inner join [user] as u on u.id = tu.userid join [training] as t on t.id = tu.trainingid where u.id = '"+userid+"' and t.endtime > datetime('now', 'localtime') and t.starttime <= datetime('now', 'localtime') ;").tables[0]; return datatabletolist(dt); } private list<traininguser> datatabletolist(datatabledt) { list<traininguser> list = new list<traininguser>(); if(dt. rows.count > 0 ) { foreach (datarow row in dt .rows) { traininguser traininguser = new traininguser(); if(row["userid" ] != null) { traininguser .userid = row["userid"].tostring(); } if(row["trainingid" ] != null) { traininguser.trainingid = row["trainingid"].tostring(); } list.add(traininguser); } } return list; }
在方法datatabletolist中,我们传入了一个datatable的对象,然后在去循环遍历每一行的对象值从而去赋值给traininguser类型对象,这只是其中的一个方法,如果我们的类型还有 training/user/project等类型的话,我们是不是就要写很多如同datatabletolist这样的方法呢? 这就体现出了这样的方式,会造成代码的冗余以及维护不便问题,那么我们使用泛型来解决
实例 2 : 使用泛型使上面的代码更见的清晰,简洁
public static list<t> tolist1<t>(datatable dt) wheret : class, new() { var prlist =new list<propertyinfo>(); type type = typeof(t); array.foreach( type.getproperties(), p => { if(dt.columns.indexof(p.name) !=-1) { prlist.add(p); } }); var oblist = new list<t>(); // system.data.sqltypes. foreach(datarow row in dt.rows) { var ob = new t(); prlist.foreach( p => { if(row[p.name] != dbnull.value) { p.setvalue(ob, row[p.name], null); } }); oblist.add(ob); } return oblist; }
在上面的这个方法中,我们定义了一个泛型方法,内部实现中是使用了反射的原理,将datatable转换为了list(反射后续随笔中总结,此处只关注泛型部分即可),我们定义了一个静态的返回值为list<t> ,前面我们说过 t : 代表任意类型(枚举除外),tolist1<t>,说明我们在调用这个方法的时候,同时要赋予方法名一个类型值,这个类型要和它的返回值类型一致(泛型是类型安全的),where : 用于限制t的条件 ,例如 where t : class,new() 表示 t 只能是一个类,或者一个类型对象,那么我们在调用的时候就可以这样来
public list<traininguser>gettrainingidbyuserid(string userid) { list<traininguser> traininguserlist = datatablehelper.tolist1<traininguser>( sqlitehelper.executedataset(system.data.commandtype.text, @" select distinct userid,trainingid from traininguser as tu inner join [user] as u on u.id = tu.userid join [training] as t on t.id = tu.trainingid where u.id = '"+ userid +"' and t.endtime > datetime('now', 'localtime') and t.starttime <= datetime('now', 'localtime') ;").tables[0]); return traininguserlist ; }
代码中的datatablehelper.tolist1<traininguser> 即为我们刚才所写的一个泛型方法,这样我们可以看到,tolist<t> 传入的类型为traininguser,同时接收者为:list<t> = list<traininguser> ,
这样即便我们后续还有training/user/project等其他的类型,我们都可以直接使用datatablehelper.tolist1<t>(datatable dt) 来进行类型转换.
2 : 提升程序的性能
泛型与非泛型相比较而言,性能要好一些,这是为什么? 首先,泛型的类型是由调用者(接收者),去直接赋值的(类型安全的), 那么就不会存在类型转换的问题,其次, 泛型减少了装箱和拆箱的过程.
实例 3 : 对于值类型泛型与非泛型的性能比较
private static void listtest() { list<int>list = new list<int>(); for(inti = 0; i < 100; i++) { list.add(i); int a = list[i]; } list =null; } private static void arrlisttest() { arraylist arr = new arraylist(); for(inti = 0; i <100; i++) { arr.add(i); int s = (int)arr[i]; } arr = null; } stopwatch sw = new stopwatch(); sw.start(); listtest(); console.writeline(" 使用泛型list执行值类型方法历时 : "+ sw.elapsed.tostring()); sw.stop(); stopwatch sw1 = new stopwatch(); sw1.start(); arrlisttest(); console.writeline(" 使用非泛型arraylist执行值类型方法历时 : "+ sw1.elapsed.tostring()); sw1.stop(); console.readline();
通过循环 100 来比较,结果为 :
我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :
泛型比非泛型执行的时间要短0.0000405秒
我们将时间在改动一下,改为 100000呢?结果为 :
这次差距为 0.0054621 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:
非泛型
从编译中我们也能看出泛型方法中,接收的为int32,非泛型为object,其次泛型不会进行装箱和拆箱操作,非泛型每次执行都要进行装箱和拆箱操作.
3 : 类型安全
在实例1 , 2 ,3 中我们都有备注说明,泛型的发送着必须要和接收者进行一致,否则会报异常 ,例如 :
实例 4 :
首页
最新文章
it 职场
前端
后端
移动端
数据库
运维
其他技术
- 导航条 -首页最新文章it 职场前端- javascript- html5- css后端- python- java- c/c++- php- .net- ruby- go移动端- android- ios数据库运维- linux- unix其他技术- git- 机器学习- 算法- 测试- 信息安全- vim
伯乐在线 > 首页 > 所有文章 > 开发 > .net泛型解析(上)
.net泛型解析(上)
2015/07/03 · 开发 · .net
分享到:6
拍摄与剪辑“怀孕日记”
ps大神通关教程
ps入门基础-魔幻调色
慕课网技术沙龙之前端专场
原文出处: 刘彬的博客
【1】:泛型介绍
泛型是c#2.0中一个重要的新特性,泛型是clr和编程语言提供的一种特殊机制,它支持另一种形式的代码重用。泛型通常用与集合以及作用于集合的方法一起使用,当然也可以单独使用.
c#是一种强类型的语言,在泛型没有被提出之前,我们在使用集合的代码的时候,每次对其进行转换都需要隐式的强制转换,我们都知道所有对象的最终基类是object,我们在每次使用object的时候,无论是变换什么类型都要对其进行强制转换。
那么有了泛型之后,使用泛型我们就无需进行转换,因为泛型根据接收者传入的参数类型,直接将每个类型更改为所传入的参数类型.
一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。 创建您自己的泛型类时,需要特别注意以下事项:
将哪些类型通用化为类型参数。
通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。 但是,太多的通用化会使其他开发人员难以阅读或理解代码。
如果存在约束,应对类型参数应用什么约束
一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。 例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。 这可以防止您的类被意外地用于值类型,并允许您对 t 使用 as 运算符以及检查空值。
是否将泛型行为分解为基类和子类。
由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。 请参见本主题后面有关从泛型基类继承的规则。
是否实现一个或多个泛型接口。
例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如 icomparable,其中 t 是您的类的类型。
【2】:泛型的表示方式
泛型可以为引用类型和值类型还有接口和委托,fcl( dll程序集,包含了.net框架下的各种dll )类中定义了一个泛型列表,用来管理一个对象集合,如果我们想要使用这个泛型列表,可以在使用时指定具体数据类型。
泛型的表示为 “t” 如:list<t>, t 表示一个未指定的数据类型,我们可以看一下fcl类中泛型的引用定义:
system.collections.generic 命名空间包含定义泛型集合的接口和类,泛型集合允许用户创建强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能。
创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡
【3】:泛型的好处
1 : 使代码更加的简洁,清晰
从前面我们也提到了,泛型具备可重用性 , 减少我们代码量, 使我们的程序更容易开发和维护,举例 :
实例 1 : 在从数据库中获取数据库的时候,我们经常会返回一个datatable类型,然后将其转换为list集合. 那么如果没有泛型的话,我会一般会这样来做.
c#
public list<traininguser>gettraininguser(string userid) { datatable dt = sqlitehelper.executedataset(system.data.commandtype.text, @" select distinct userid,trainingid from traininguser as tu inner join [user] as u on u.id = tu.userid join [training] as t on t.id = tu.trainingid where u.id = '"+userid+"' and t.endtime > datetime('now', 'localtime') and t.starttime <= datetime('now', 'localtime') ;").tables[0]; return datatabletolist(dt); } private list<traininguser> datatabletolist(datatabledt) { list<traininguser> list = new list<traininguser>(); if(dt. rows.count > 0 ) { foreach (datarow row in dt .rows) { traininguser traininguser = new traininguser(); if(row["userid" ] != null) { traininguser .userid = row["userid"].tostring(); } if(row["trainingid" ] != null) { traininguser.trainingid = row["trainingid"].tostring(); } list.add(traininguser); } } return list; }
在方法datatabletolist中,我们传入了一个datatable的对象,然后在去循环遍历每一行的对象值从而去赋值给traininguser类型对象,这只是其中的一个方法,如果我们的类型还有 training/user/project等类型的话,我们是不是就要写很多如同datatabletolist这样的方法呢? 这就体现出了这样的方式,会造成代码的冗余以及维护不便问题,那么我们使用泛型来解决
实例 2 : 使用泛型使上面的代码更见的清晰,简洁
c#
public static list<t> tolist1<t>(datatable dt) wheret : class, new() { var prlist =new list<propertyinfo>(); type type = typeof(t); array.foreach( type.getproperties(), p => { if(dt.columns.indexof(p.name) !=-1) { prlist.add(p); } }); var oblist = new list<t>(); // system.data.sqltypes. foreach(datarow row in dt.rows) { var ob = new t(); prlist.foreach( p => { if(row[p.name] != dbnull.value) { p.setvalue(ob, row[p.name], null); } }); oblist.add(ob); } return oblist; }
在上面的这个方法中,我们定义了一个泛型方法,内部实现中是使用了反射的原理,将datatable转换为了list(反射后续随笔中总结,此处只关注泛型部分即可),我们定义了一个静态的返回值为list<t> ,前面我们说过 t : 代表任意类型(枚举除外),tolist1<t>,说明我们在调用这个方法的时候,同时要赋予方法名一个类型值,这个类型要和它的返回值类型一致(泛型是类型安全的),where : 用于限制t的条件 ,例如 where t : class,new() 表示 t 只能是一个类,或者一个类型对象,那么我们在调用的时候就可以这样来
c#
public list<traininguser>gettrainingidbyuserid(string userid) { list<traininguser> traininguserlist = datatablehelper.tolist1<traininguser>( sqlitehelper.executedataset(system.data.commandtype.text, @" select distinct userid,trainingid from traininguser as tu inner join [user] as u on u.id = tu.userid join [training] as t on t.id = tu.trainingid where u.id = '"+ userid +"' and t.endtime > datetime('now', 'localtime') and t.starttime <= datetime('now', 'localtime') ;").tables[0]); return traininguserlist ; }
代码中的datatablehelper.tolist1<traininguser> 即为我们刚才所写的一个泛型方法,这样我们可以看到,tolist<t> 传入的类型为traininguser,同时接收者为:list<t> = list<traininguser> ,
这样即便我们后续还有training/user/project等其他的类型,我们都可以直接使用datatablehelper.tolist1<t>(datatable dt) 来进行类型转换.
2 : 提升程序的性能
泛型与非泛型相比较而言,性能要好一些,这是为什么? 首先,泛型的类型是由调用者(接收者),去直接赋值的(类型安全的), 那么就不会存在类型转换的问题,其次, 泛型减少了装箱和拆箱的过程.
实例 3 : 对于值类型泛型与非泛型的性能比较
c#
private static void listtest() { list<int>list = new list<int>(); for(inti = 0; i < 100; i++) { list.add(i); int a = list[i]; } list =null; } private static void arrlisttest() { arraylist arr = new arraylist(); for(inti = 0; i <100; i++) { arr.add(i); int s = (int)arr[i]; } arr = null; } stopwatch sw = new stopwatch(); sw.start(); listtest(); console.writeline(" 使用泛型list执行值类型方法历时 : "+ sw.elapsed.tostring()); sw.stop(); stopwatch sw1 = new stopwatch(); sw1.start(); arrlisttest(); console.writeline(" 使用非泛型arraylist执行值类型方法历时 : "+ sw1.elapsed.tostring()); sw1.stop(); console.readline();
通过循环 100 来比较,结果为 :
我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :
泛型比非泛型执行的时间要短0.0000405秒
我们将时间在改动一下,改为 100000呢?结果为 :
这次差距为 0.0054621 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:
非泛型
从编译中我们也能看出泛型方法中,接收的为int32,非泛型为object,其次泛型不会进行装箱和拆箱操作,非泛型每次执行都要进行装箱和拆箱操作.
3 : 类型安全
在实例1 , 2 ,3 中我们都有备注说明,泛型的发送着必须要和接收者进行一致,否则会报异常 ,例如 :
实例 4 :
将一个泛型算法应用于一个具体的类型时,编译器和clr能理解开发人员的意图,并保证只有与指定数据类型兼容的对象才能随同算法使用,若试图使用不兼容类型的一个对象,会造成编译时错误,或者运行时抛出异常
更多.net泛型解析。