泛型的引入:微软在 .net frameWork 2.0 框架引入了泛型。 泛型的引入解决了代码重复了的问题,将公共逻辑抽象化;增加了代码的性能,泛型通过使用类型占位符预编译在实际调用时传递具体类型参数,进而减少值类型和引用类型的相互装箱 / 拆箱。
- 引入泛型
- 声明使用泛型
- 泛型好处对比性能
- 泛型类,泛型方法,泛型接口,泛型委托
- 泛型约束
- 泛型的逆变,协变 out协变covariant 修饰返回值,in 逆变 contravariant 修饰传入值
- 泛型缓存
泛型的声明在没有使用泛型的情况下编写可以接收多种类型的实现相同逻辑的函数通常有两种方式。1,需要什么类型就定义一个接收固定类型的方法,各自方法实现一遍相同的逻辑。2,定义一个函数接收object类型参数。代码如下
1 2 public class CommonMethod 3 { 4 public static void ShowInt(int iParameter) 5 { 6 Console.WriteLine("This is {0},parameter {1},type={2}", 7 typeof(CommonMethod).Name, iParameter, iParameter.GetType().Name); 8 } 9 public static void ShowString(string sParameter) 10 { 11 Console.WriteLine("This is {0},parameter {1},type={2}", 12 typeof(CommonMethod).Name, sParameter, sParameter.GetType().Name); 13 } 14 public static void ShowDateTime(DateTime dtParameter) 15 { 16 Console.WriteLine("This is {0},parameter {1},type={2}", 17 typeof(CommonMethod).Name, dtParameter, dtParameter.GetType().Name); 18 } 19 20 21 /// <summary> 22 /// object是所有类型的父类 23 /// 通过继承,子类拥有父类一切的属性和行为,任何父类出现的地方都可以用子类来代替 24 /// 传递值类型的时候会产生装箱操作 25 /// </summary> 26 /// <param name="obj"></param> 27 public static void ShowObject(object obj) { 28 Console.WriteLine("This is {0},parameter {1},type={2}", 29 typeof(CommonMethod).Name, obj, obj.GetType().Name); 30 }
用第一种方式和第二种方式都可以解决问题。那么他们有各自的缺点。
第一种方式:如果传递十种类型的参数那么就需要定义是个方法,内部逻辑相同这样会产生重复劳动和大量的重复代码。
第二种方式:因为参数为object类型如果传递了值类型就会产生装箱操作,产生程序性能的损耗。
第三种方式:泛型。通过泛型可以解决以上的需求: 传入参数不确定,内部逻辑相同,不会产生性能方面的损耗。
声明使用泛型方法
1 //泛型方法 --- 有相同逻辑的地方,类型不同或类型不确定的情况下,提高代码重用性 2 /// <summary> 3 /// 延迟声明,在声明方法的时候没有指定参数类型,在调用的时候再指定 4 /// 编译的时候,类型参数编译为占位符 5 /// 程序运行的时候jit即时编译的时候编译为真实类型 6 /// </summary> 7 /// <typeparam name="T">类型参数</typeparam> 8 /// <param name="tParameter">实际参数</param> 9 public static void Show<T>(T tParameter) { 10 Console.WriteLine("This is {0},parameter {1},type={2}", 11 typeof(CommonMethod).Name, tParameter, tParameter.GetType().Name); 12 }
使用泛型方法和普通方法和object方法的性能对比
1 public class Monitor 2 { 3 int iValue = 12345; 4 static long commonSeconds = 0; //普通方法运行时间 5 static long objectSeconds = 0; //object方法运行时间 6 static long genericSeconds = 0; //泛型方法运行时间 7 8 public static void ShowMonitor() 9 { 10 { 11 12 Stopwatch commonWatch = new Stopwatch(); 13 commonWatch.Start(); 14 for (int i = 0; i < 1000000000; i++) 15 { 16 commonMethod(i); 17 } 18 commonWatch.Stop(); 19 commonSeconds = commonWatch.ElapsedMilliseconds; 20 } 21 22 { 23 24 Stopwatch objectWatch = new Stopwatch(); 25 objectWatch.Start(); 26 for (int i = 0; i < 1000000000; i++) 27 { 28 objectMethod(i); 29 } 30 objectWatch.Stop(); 31 objectSeconds = objectWatch.ElapsedMilliseconds; 32 } 33 34 { 35 36 Stopwatch genericWatch = new Stopwatch(); 37 genericWatch.Start(); 38 for (int i = 0; i < 1000000000; i++) 39 { 40 genericMethod(i); 41 } 42 genericWatch.Stop(); 43 genericSeconds = genericWatch.ElapsedMilliseconds; 44 } 45 Console.WriteLine("普通方法的运行时间为:{0},object方法的运行时间为:{1},泛型方法的运行时间为:{2}",commonSeconds,objectSeconds,genericSeconds); 46 } 47 48 49 public static void commonMethod(int iValue) { } 50 public static void objectMethod(object objValue) { } 51 52 public static void genericMethod<T>(T tValue) { } 53 }
代码中定义三个方法,分别为普通方法,object方法,泛型方法
依次进行十亿次的调用传递值类型参数进行函数调用,然后分别记录下执行时间。通过对比可以看出普通方法和泛型方法的执行时间相近。object方法执行时间几乎为前两者的2倍,这是装箱带来的副作用。
泛型方法在代码整洁度和维护成本上又超过了普通方法。泛型方法应该是同等需求下的最优之选。
泛型的应用,泛型方法,泛型类,泛型接口,泛型委托
1 /// <summary> 2 /// 泛型类 参考List<> 3 /// 相当于在GenericClass类范围内声明了三个局部类型 A/B/C 4 /// </summary> 5 /// <typeparam name="A"></typeparam> 6 /// <typeparam name="B"></typeparam> 7 /// <typeparam name="C"></typeparam> 8 public class GenericClass<A,B,C> 9 { 10 public void Show(A a) { //可以直接使用泛型类型 11 12 } 13 } 14 15 public interface iInterface<T> { 16 void Study(T t); 17 T Get(T t); 18 } 19 20 public delegate T ShowDelegate<T>(T t);
泛型约束
如果只是定义了一个泛型方法,不对传入的泛型类型参数做约束那么代码就不可控进而会出现不可预期bug。
泛型类型的约束有以下5类:无参数构造函数约束,引用类型约束,值类型约束,基类约束,接口约束。对应代码为(new(),class,struct,基类名,接口名)
以下代码展示以上各种约束。
1 public interface iSports 2 { 3 void Play(); 4 } 5 public class People 6 { 7 public int ID { get; set; } 8 public string Name { get; set; } 9 10 public virtual void Hi() { 11 Console.WriteLine("你好"); 12 } 13 } 14 15 public class Chinese : People, iSports 16 { 17 public void Play() 18 { 19 Console.WriteLine("打乒乓球。"); 20 } 21 public string tradition = "仁义礼智信,温良恭俭让"; 22 23 public override void Hi() 24 { 25 Console.WriteLine("吃了没"); 26 } 27 } 28 29 public class HuBei : Chinese { 30 public void Majiang() { 31 Console.WriteLine("打麻将"); 32 } 33 } 34 35 public class Japanese : iSports 36 { 37 public int ID { get; set; } 38 public string Name { get; set; } 39 public void Play() 40 { 41 Console.WriteLine("打乒乓球"); 42 } 43 public void Hi() { 44 Console.WriteLine("日本打招呼"); 45 } 46 }
1 /// <summary> 2 /// 泛型约束:积累约束/接口约束/引用类型约束/值类型约束/无参数构造函数约束(new()) 3 /// </summary> 4 public class Constriant 5 { 6 public static void Show<T>(T tParameter) where T:People 7 { 8 Console.WriteLine("This is {0},parameter {1},type={2}", 9 typeof(CommonMethod).Name, tParameter, tParameter.GetType().Name); 10 Console.WriteLine(tParameter.ID); 11 Console.WriteLine(tParameter.Name); 12 tParameter.Hi(); 13 } 14 15 /// <summary> 16 /// 接口约束,传递过来的类型参数必须实现了iSports接口 17 /// </summary> 18 /// <typeparam name="T"></typeparam> 19 /// <param name="tParameter"></param> 20 public static void ShowInterface<T>(T tParameter) where T:iSports 21 { 22 Console.WriteLine("This is {0},parameter {1},type={2}", 23 typeof(CommonMethod).Name, tParameter, tParameter.GetType().Name); 24 tParameter.Play(); 25 } 26 27 /// <summary> 28 /// 引用类型约束 29 /// </summary> 30 /// <typeparam name="T"></typeparam> 31 /// <returns></returns> 32 public static T Get<T>() 33 where T:class 34 { 35 return default(T); 36 } 37 /// <summary> 38 /// 值类型约束 39 /// </summary> 40 /// <typeparam name="T"></typeparam> 41 /// <returns></returns> 42 public static T GetStruct<T>() 43 where T : struct 44 { 45 return default(T); 46 } 47 48 public static T GetConstructor<T>() 49 where T:new() //无参数构造函数约束 50 { 51 T t = new T(); 52 return default(T); 53 } 54 }
1 Console.WriteLine("**************泛型约束:基类约束**************"); 2 People people = new People() { 3 ID = 1, 4 Name ="zhangsan" 5 }; 6 Chinese chinese = new Chinese() { 7 ID = 2, 8 Name = "lisi" 9 }; 10 HuBei hubei = new HuBei() { 11 ID=3, 12 Name = "xl" 13 }; 14 Constriant.Show(people); 15 Constriant.Show(chinese); 16 Constriant.Show(hubei); //调用基类约束的泛型方法,传递的类型必须是基类或者子类 17 18 Japanese japanese = new Japanese() { 19 ID = 4, 20 Name ="小泉" 21 }; 22 //Constriant.Show(japanese);//japanese 没有从people派生,不符合基类约束不能通过编译 23 Console.WriteLine("*********泛型约束:接口约束************"); 24 //Constriant.ShowInterface(people); people类没有实现iSport接口,不符合接口约束不能通过编译 25 Constriant.ShowInterface(chinese); 26 Constriant.ShowInterface(hubei); 27 Constriant.ShowInterface(japanese); //japanese实现了iSport接口,可以编译通过
在泛型约束调用时可见,因为japanese没有从people类中派生,所以在调用有泛型类型People基类约束时候会报错,这样在编译时期就能避免类型不对应而导致的错误。
泛型的逆变,协变
泛型缓存