打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
.NET泛型解析(上)


【1】:泛型介绍


泛型是C#2.0中一个重要的新特性,泛型是CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用。泛型通常用与集合以及作用于集合的方法一起使用,当然也可以单独使用.


C#是一种强类型的语言,在泛型没有被提出之前,我们在使用集合的代码的时候,每次对其进行转换都需要隐式的强制转换,我们都知道所有对象的最终基类是object,我们在每次使用object的时候,无论是变换什么类型都要对其进行强制转换。

那么有了泛型之后,使用泛型我们就无需进行转换,因为泛型根据接收者传入的参数类型,直接将每个类型更改为所传入的参数类型.


一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。 创建您自己的泛型类时,需要特别注意以下事项:


  • 将哪些类型通用化为类型参数。


通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。 但是,太多的通用化会使其他开发人员难以阅读或理解代码。


  • 如果存在约束,应对类型参数应用什么约束


一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。 例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。 这可以防止您的类被意外地用于值类型,并允许您对 T 使用 as 运算符以及检查空值。


  • 是否将泛型行为分解为基类和子类。


由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。 请参见本主题后面有关从泛型基类继承的规则。


  • 是否实现一个或多个泛型接口。


例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如 IComparable,其中 T 是您的类的类型。


【2】:泛型的表示方式


泛型可以为引用类型和值类型还有接口和委托,FCL( DLL程序集,包含了.NET框架下的各种DLL )类中定义了一个泛型列表,用来管理一个对象集合,如果我们想要使用这个泛型列表,可以在使用时指定具体数据类型。

泛型的表示为 “T” 如:List, T 表示一个未指定的数据类型,我们可以看一下FCL类中泛型的引用定义:

System.Collections.Generic 命名空间包含定义泛型集合的接口和类,泛型集合允许用户创建强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能。

创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡


【3】:泛型的好处


1 : 使代码更加的简洁,清晰


从前面我们也提到了,泛型具备可重用性 , 减少我们代码量, 使我们的程序更容易开发和维护,举例 :


实例 1 : 在从数据库中获取数据库的时候,我们经常会返回一个DataTable类型,然后将其转换为List集合. 那么如果没有泛型的话,我会一般会这样来做.


public ListGetTrainingUser(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')="">

return DataTableToList(dt);

}


private List DataTableToList(DataTabledt)

{

List list = new List();

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 ToList1(DataTable dt) whereT : class, new()

{

var prlist =new List();

Type type = typeof(T);

Array.ForEach(

type.GetProperties(),

p =>

{

if(dt.Columns.IndexOf(p.Name) !=-1)

{

prlist.Add(p);

}

});

var oblist = new List();


// 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 : 代表任意类型(枚举除外),ToList1,说明我们在调用这个方法的时候,同时要赋予方法名一个类型值,这个类型要和它的返回值类型一致(泛型是类型安全的),Where : 用于限制T的条件 ,例如 where T : class,new() 表示 T 只能是一个类,或者一个类型对象,那么我们在调用的时候就可以这样来


public ListGetTrainingIdByUserId(string userId)

{

List trainingUserList = DataTableHelper.ToList1(

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')="">

return trainingUserList ;

}


代码中的DataTableHelper.ToList1 即为我们刚才所写的一个泛型方法,这样我们可以看到,ToList 传入的类型为TrainingUser,同时接收者为:List = List ,

这样即便我们后续还有Training/User/Project等其他的类型,我们都可以直接使用DataTableHelper.ToList1(DataTable dt) 来进行类型转换.


2 : 提升程序的性能


泛型与非泛型相比较而言,性能要好一些,这是为什么? 首先,泛型的类型是由调用者(接收者),去直接赋值的(类型安全的), 那么就不会存在类型转换的问题,其次, 泛型减少了装箱和拆箱的过程.


实例 3 : 对于值类型泛型与非泛型的性能比较


private static void ListTest()

{

Listlist = new List();

for(inti = 0; i < 100;="">

{

list.Add(i);

int a = list[i];

}

list =null;

}

private static void ArrListTest()

{

ArrayList arr = new ArrayList();

for(inti = 0; i <100;>

{

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能理解开发人员的意图,并保证只有与指定数据类型兼容的对象才能随同算法使用,若试图使用不兼容类型的一个对象,会造成编译时错误,或者运行时抛出异常


此篇至此,下篇主要知识点 :


1、泛型方法

2、泛型接口

3、泛型约束(主要约束,次要约束,构造器约束)

4、泛型类型转型

5、泛型委托

6、泛型和反射

7、泛型和属性


参考资料 :


CLR C# Via

深入理解C#

https://msdn.microsoft.com/zh-cn/library/sz6zd40f.aspx


https://msdn.microsoft.com/zh-cn/library/0x6a29h6.aspx


原文出处: 刘彬

原文链接: http://www.cnblogs.com/DeepLearing/p/4554867.html



本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
公司新来了一个同事,把Lambda表达式运用的炉火纯青!
C# DataTable 转 List(大家进来讨论讨论)
模式识别:K_近邻法
datatable与list的互相转换
C# DataSet转换为List
DataTable转成实体列表 和 DataRow转成实体类
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服