【1】:泛型介绍
泛型是C#2.0中一个重要的新特性,泛型是CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用。泛型通常用与集合以及作用于集合的方法一起使用,当然也可以单独使用.
一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。 创建您自己的泛型类时,需要特别注意以下事项:
-
将哪些类型通用化为类型参数。
通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。 但是,太多的通用化会使其他开发人员难以阅读或理解代码。
-
如果存在约束,应对类型参数应用什么约束
一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。 例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。 这可以防止您的类被意外地用于值类型,并允许您对 T 使用 as 运算符以及检查空值。
-
是否将泛型行为分解为基类和子类。
由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。 请参见本主题后面有关从泛型基类继承的规则。
-
是否实现一个或多个泛型接口。
例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如 ,其中 T 是您的类的类型。
【2】:泛型的表示方式
System.Collections.Generic 命名空间包含定义泛型集合的接口和类,泛型集合允许用户创建强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能。创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡
【3】:泛型的好处
1 public ListGetTrainingUser(string userId) 2 { 3 DataTable dt = 4 SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, 5 @" 6 SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU 7 INNER JOIN [USER] AS U 8 ON U.ID = TU.USERID 9 JOIN [TRAINING] AS T10 ON T.ID = TU.TRAININGID11 WHERE U.ID = '"+userId+"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0];12 return DataTableToList(dt);13 }14 15 private List DataTableToList(DataTabledt)16 {17 List list = new List ();18 if(dt. Rows.Count > 0 )19 {20 foreach (DataRow row in dt .Rows)21 {22 TrainingUser trainingUser = new TrainingUser();23 if(row["UserId" ] != null)24 {25 trainingUser .UserId = row["UserId"].ToString();26 }27 if(row["TrainingId" ] != null)28 {29 trainingUser.TrainingId = row["TrainingId"].ToString();30 }31 list.Add(trainingUser);32 }33 }34 return list;35 }
1 public static ListToList1 (DataTable dt) whereT : class, new() 2 { 3 var prlist =new List (); 4 Type type = typeof(T); 5 Array.ForEach( 6 type.GetProperties(), 7 p => 8 { 9 if(dt.Columns.IndexOf(p.Name) !=-1)10 {11 prlist.Add(p);12 }13 });14 var oblist = new List ();15 16 // System.Data.SqlTypes.17 foreach(DataRow row in dt.Rows)18 {19 var ob = new T();20 prlist.ForEach(21 p =>22 {23 if(row[p.Name] != DBNull.Value)24 {25 p.SetValue(ob, row[p.Name], null);26 }27 });28 oblist.Add(ob);29 }30 31 return oblist;32 }
在上面的这个方法中,我们定义了一个泛型方法,内部实现中是使用了反射的原理,将DataTable转换为了List(反射后续随笔中总结,此处只关注泛型部分即可),我们定义了一个静态的返回值为List<T> ,前面我们说过 T : 代表任意类型(枚举除外),ToList1<T>,说明我们在调用这个方法的时候,同时要赋予方法名一个类型值,这个类型要和它的返回值类型一致(泛型是类型安全的),Where : 用于限制T的条件 ,例如 where T : class,new() 表示 T 只能是一个类,或者一个类型对象,那么我们在调用的时候就可以这样来
1 public ListGetTrainingIdByUserId(string userId) 2 { 3 List trainingUserList = DataTableHelper.ToList1 ( 4 SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, 5 @" 6 SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU 7 INNER JOIN [USER] AS U 8 ON U.ID = TU.USERID 9 JOIN [TRAINING] AS T10 ON T.ID = TU.TRAININGID11 WHERE U.ID = '"+ userId +"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0]);12 return trainingUserList ;13 }
1 private static void ListTest() 2 { 3 List list = new List (); 4 for(inti = 0; i < 100; i++) 5 { 6 list.Add(i); 7 int a = list[i]; 8 } 9 list =null;10 }11 private static void ArrListTest()12 {13 ArrayList arr = new ArrayList();14 for(inti = 0; i <100; i++)15 {16 arr.Add(i);17 int s = (int)arr[i];18 }19 arr = null;20 }21 22 Stopwatch sw = new Stopwatch();23 sw.Start();24 ListTest();25 Console.WriteLine(" 使用泛型List执行值类型方法历时 : "+ sw.Elapsed.ToString());26 sw.Stop();27 28 Stopwatch sw1 = new Stopwatch();29 sw1.Start();30 ArrListTest();31 Console.WriteLine(" 使用非泛型ArrayList执行值类型方法历时 : "+ sw1.Elapsed.ToString());32 sw1.Stop();33 Console.ReadLine();
通过循环 100 来比较,结果为 :
我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :
将一个泛型算法应用于一个具体的类型时,编译器和CLR能理解开发人员的意图,并保证只有与指定数据类型兼容的对象才能随同算法使用,若试图使用不兼容类型的一个对象,会造成编译时错误,或者运行时抛出异常
此篇至此,下篇主要知识点 :
1、泛型方法2、泛型接口3、泛型约束(主要约束,次要约束,构造器约束)4、泛型类型转型5、泛型委托6、泛型和反射7、泛型和属性