Net从2.0版 开始支持泛型。泛型允许在编译时实现类型安全。允许创建一个数据结构而不限于特定的数据类型。当使用该数据结构时,编译器保证它使用的类型与类型安全是相一致的。泛型提供了类型安全,但是没有造成任何性能损失和代码臃肿。泛型不仅限于类,还可用于接口、方法、委托…。

为什么使用泛型

  • 类型安全
  • 避免装箱拆箱带来的性能损耗
  • 二进制代码重用

    1. ArrayList arrs = new ArrayList
    2. {
    3. "wang"
    4. 100
    5. };

    这里如果将 item 设置为 int 类型遍历集合,会抛出异常,因为集合中存在string 类型项

    1. foreach (var item in arrs)
    2. {
    3. Console.WriteLine(item);
    4. }

    遍历时不可避免装箱/拆箱操作

    1. foreach (var item in arrs)
    2. {
    3. Console.WriteLine(item);
    4. }

    非泛型集合

    1. var arr1 = new ArrayList();
    2. arr1.Add(10);//Add方法参数值是object类型,这里将int->object
    3. arr1.Add("wang");//Add方法参数值是object类型,这里将int->object
    4. foreach (int item in arr1)//这里如果用int类型遍历集合,就挂掉了
    5. {
    6. Console.WriteLine(item);//读取时要进行拆箱
    7. }

    泛型集合

    1. /*泛型集合在使用时定义好类型,之后就不存在装箱拆箱;
    2. * 因为已经定义当前集合只能存入int类型,也就不能存入其他类型
    3. * 编译时就会报错,错误应及早发现
    4. */
    5. var arr2 = new List<int>();
    6. arr2.Add(10);
    7. foreach (var item in arr2)
    8. {
    9. Console.WriteLine(item);
    10. }

    通过参数化类型来实现在同一份代码上操作多种数据类型,利用“参数化类型”将类型抽象化,从而实现灵活的复用。每个集合的详细规范可以在System.Collection.Generic 名称空间下找到。

    泛型方法

    1. public class Print
    2. {
    3. public void PrintType<Tentity>() where Tentity : new()
    4. {
    5. Tentity t = new Tentity();
    6. Console.WriteLine(t.GetType());
    7. }
    8. }

    泛型类

    1. public class MyArray<T> where T : new()
    2. {
    3. private readonly T[] array;
    4. public MyArray(int size)
    5. {
    6. array = new T[size + 1];
    7. }
    8. public T GetItem(int index)
    9. {
    10. return array[index];
    11. }
    12. public void SetItem(int index T value)
    13. {
    14. array[index] = value;
    15. }
    16. }

    default

    default 用于将泛型类型初始化为 null0

    1. T t = default(T);

    泛型接口

    1. public interface IPrint<T>
    2. {
    3. void Add(T t);
    4. }

    泛型委托

    1. public delegate void Print<T>(T t);

    泛型在声明的时候可以不指定具体的类型,但是在使用的时候必须指定具体类型

    1. Print<int> iPrint = new Print<int>();

    这里的类型是int,如果存在继承并且子类也是泛型的,那么继承的时候可以不指定具体类型 ```csharp class Print { public T t; }

//这里继承时没有指定泛型类型 class ConsolePrint : Print {

}

  1. 同上,类实现泛型接口也是如此。
  2. <a name="8f3b5c3e"></a>
  3. ## 泛型约束
  4. 所谓的泛型约束,就是约束类型 `T` ,使 `T` 必须遵循一定的规则。比如 `T` 必须继承自某个类,或者 `T` 必须实现某个接口等。
  5. | **类型** | **描述** |
  6. | --- | :--- |
  7. | T:calss | 类型参数必须是引用类型,包括任何类、接口、委托或数组类型 |
  8. | T:stauct | 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型 |
  9. | T:new() | 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定 |
  10. | T:<基类名> | 类型参数必须是指定的基类或派生自指定的基类 |
  11. | T:<接口名称> | 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的 |
  12. | T1:T2 | 类型T1派生自泛型类型T2,该约束也被称为裸类型约束 |
  13. ```csharp
  14. class Person
  15. {
  16. public static void PintSayHello()
  17. {
  18. }
  19. }
  20. interface ISport
  21. {
  22. void Sport();
  23. }
  24. /// <summary>
  25. /// 约束T必须是Person类型或者是Person的子类并且实现了ISport接口且T类型中必须有无参构造函数
  26. /// </summary>
  27. /// <typeparam name="T"></typeparam>
  28. class Show<T> where T : Person, ISport, new()
  29. {
  30. }
  31. /// <summary>
  32. /// 约束T必须是引用类型并且实现了ISport接口且T类型中必须有无参构造函数
  33. /// </summary>
  34. /// <typeparam name="T"></typeparam>
  35. class Show2<T> where T : class, ISport, new()
  36. {
  37. }
  38. /// <summary>
  39. /// 约束T必须是值类型并且实现了ISport接口
  40. /// </summary>
  41. /// <typeparam name="T"></typeparam>
  42. class Show3<T> where T : struct, ISport
  43. {
  44. }

注意:泛型约束可以同时约束多个,有多个泛型约束时,new() 约束一定是在最后

泛型协变与逆变

协变和逆变在.NET 4.0的时候出现,只能放在接口或者委托的泛型参数前

  • out 协变 covariant ,用来修饰返回值
  • in:逆变 contravariant ,用来修饰传入参数

在C#中声明泛型接口时,可以使用 inout 参数来控制这个泛型是协变还是逆变的(逆变有时也被翻译成抗变)协变和逆变是用来描述如果泛型存在继承关系时,两个泛型类是否能够直接赋值的问题。比如派生泛型 IInterface<Child> 是否能被赋值给 IInterface<Parent>

协变:只能是方法参数

  1. public interface ICustomerListIn<in T>
  2. {
  3. void Show(T t);
  4. }
  1. /// <summary>
  2. /// 泛型协变(子类到父类的转换)
  3. /// </summary>
  4. /// <remarks>
  5. /// out:协变covariant,用来修饰返回值
  6. /// </remarks>
  7. [TestMethod]
  8. public void GenericityCovariant()
  9. {
  10. // 直接声明Animal类
  11. Animal animal = new Animal();
  12. Console.WriteLine($"{animal.GetType()}");
  13. // 直接声明Cat类
  14. Cat cat = new Cat();
  15. Console.WriteLine($"{cat.GetType()}");
  16. // 声明子类对象指向父类
  17. Animal animal2 = new Cat();
  18. Console.WriteLine($"{animal2.GetType()}");
  19. // 声明Animal类的集合
  20. List<Animal> listAnimal = new List<Animal>();
  21. foreach (var item in listAnimal)
  22. {
  23. Console.WriteLine($"{item.GetType()}");
  24. }
  25. // 声明Cat类的集合
  26. List<Cat> listCat = new List<Cat>();
  27. foreach (var item in listCat)
  28. {
  29. Console.WriteLine($"{item.GetType()}");
  30. }
  31. #if debug
  32. // 一只Cat属于Animal,一群Cat也应该属于Animal。但实际上这样声明是错误的:因为List<Cat>和List<Animal>之间没有父子关系
  33. List<Animal> list = new List<Cat>();
  34. #endif
  35. // 泛型协变,IEnumerable泛型参数类型使用了out修饰
  36. ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
  37. Console.WriteLine($"{customerList1.GetType()}");
  38. ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>();
  39. Console.WriteLine($"{customerList2.GetType()}");
  40. Assert.IsTrue(true);
  41. }

逆变:只能是返回值

  1. public interface ICustomerListIn<in T>
  2. {
  3. void Show(T t);
  4. }
  1. /// <summary>
  2. /// 泛型逆变(父类到子类的转换)
  3. /// </summary>
  4. /// <remarks>
  5. /// int:协变contravariant,用来修饰返回值(子类到父类的转换)
  6. /// </remarks>
  7. [TestMethod]
  8. public void GenericityContravariant()
  9. {
  10. // 直接声明Animal类
  11. Animal animal = new Animal();
  12. Console.WriteLine($"{animal.GetType()}");
  13. // 直接声明Cat类
  14. Cat cat = new Cat();
  15. Console.WriteLine($"{cat.GetType()}");
  16. // 声明子类对象指向父类
  17. Animal animal2 = new Cat();
  18. Console.WriteLine($"{animal2.GetType()}");
  19. // 声明Animal类的集合
  20. List<Animal> listAnimal = new List<Animal>();
  21. foreach (var item in listAnimal)
  22. {
  23. Console.WriteLine($"{item.GetType()}");
  24. }
  25. // 声明Cat类的集合
  26. List<Cat> listCat = new List<Cat>();
  27. foreach (var item in listCat)
  28. {
  29. Console.WriteLine($"{item.GetType()}");
  30. }
  31. ICustomerListIn<Cat> customerListCat1 = new CustomerListIn<Cat>();
  32. Console.WriteLine($"{customerListCat1.GetType()}");
  33. ICustomerListIn<Cat> customerListCat2 = new CustomerListIn<Animal>();
  34. Console.WriteLine($"{customerListCat2.GetType()}");
  35. Assert.IsTrue(true);
  36. }

高性能泛型缓存

静态字典缓存和常用的泛型缓存的性能相比,泛型缓存性能是非常优异的。泛型缓存是 JIT 产生全新的类,内存直接分配,由CPU查找内存地址。静态字典缓存需要根据地址去寻址查找。

  1. /// <summary>
  2. /// 类中的静态类型无论实例化多少次,在内存中只会有一个
  3. /// 静态构造函数只会执行一次
  4. /// 在泛型类中,T类型不同
  5. /// 每个不同的T类型,都会产生一个不同的副本,所以会产生不同的静态属性、不同的静态构造函数
  6. /// </summary>
  7. /// <typeparam name="T"></typeparam>
  8. gma warning disable S1118 // Utility classes should not have public constructors
  9. public class GenericCache<T>
  10. gma warning restore S1118 // Utility classes should not have public constructors
  11. {
  12. gma warning disable S3963 // "static" fields should be initialized inline
  13. static GenericCache()
  14. {
  15. _CachedValue = string.Format("{0}_{1}"
  16. typeof(T).FullName DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
  17. }
  18. gma warning restore S3963 // "static" fields should be initialized inline
  19. gma warning disable S2743 // Static fields should not be used in generic types
  20. private static readonly string _CachedValue = "";
  21. gma warning restore S2743 // Static fields should not be used in generic types
  22. public static string GetCache()
  23. {
  24. return _CachedValue;
  25. }
  26. }
  1. /// <summary>
  2. /// 泛型会为不同的类型都创建一个副本
  3. /// 所以静态构造函数会执行4次,而且每次静态属性的值都是一样的
  4. /// 利用泛型的这一特性,可以实现缓存
  5. /// 注意:只能为不同的类型缓存一次。泛型缓存比字典缓存效率高。泛型缓存不能主动释放
  6. /// </summary>
  7. [TestMethod]
  8. public void GenericityCache()
  9. {
  10. for (int i = 0; i < 5; i++)
  11. {
  12. Console.WriteLine(GenericCache<int>.GetCache());
  13. Console.WriteLine(GenericCache<long>.GetCache());
  14. Console.WriteLine(GenericCache<DateTime>.GetCache());
  15. Console.WriteLine(GenericCache<string>.GetCache());
  16. }
  17. Assert.IsTrue(true);
  18. }

image.png

参考

https://www.cnblogs.com/dotnet261010/p/9034594.html
https://www.cnblogs.com/yilezhu/p/10029782.html
https://www.cnblogs.com/aehyok/p/3384637.html