# 泛型

官方文档

# 定义
  • 泛型是为所存储或使用的一个或多个类型具有占位符(类型形参)的类、结构、接口和方法,相当于一个” 模板 “。

  • T:代表类型参数,指在创建泛型类型的实例时指定的特定类型占位符

  • 在初始化这种类或者方法之前,这些类或者方法会延迟指定一个或者多个类型,在其他类中使用泛型,而不会产生运行时的转换或装箱操作的风险。

# 优点
  • 可重用性提高(无需从基类型继承,无需重写成员),类型安全(编译时强制判断数据类型),提高性能 (更好的存储和操作值类型,避免装箱操作的成本)
  • 可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托
  • 可以对泛型类进行约束以访问特定数据类型的方法
  • 在泛型数据类型中所用类型的信息可在运行时通过使用反射来获取 动态方法
# 缺点
  • NET 不支持上下文绑定的泛型类型
  • 枚举不能具有泛型类型形参
  • 轻量动态方法不能是泛型
  • 包含在泛型类型中的嵌套类型不能被实例化
# 协变与逆变
  • 协变:关键字 out,只能用作方法的返回值类型,能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型(如:string->object (子类到父类的转换))
  • 逆变:关键字 in,智能用工作方法的入参类型。能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型(object->string (父类到子类的转换))

协变逆变中的协逆是相对于继承关系的继承链方向而言的,在面向对象中父类和子类有一个关系,子类可以向上转为基类,但是基类不能向下转为子类。

# Func<T> 与 Action<T>
  • Func<T>

只有输出,出参只有协变,返回结果只能返回 T 类的父类,例如 object a = object (string),这肯定是不行的。

public delegate TResult Func<out TResult>();
  • Action<T>

只有输入,入参只有逆变,入参只能是 T 类及 T 类的子类,原因是不能把父类当做子类来使用。

public delegate void Action<in T>(T obj);
# 泛型约束
  • where T : class ,类型参数必须是引用类型。
  • where T : new() ,类型参数必须具有公共无参数构造函数。
  • where T : U ,T 必须是 U 类型或者 U 的派生类。
  • where T : <基类名>,类型参数必须是指定的基类或派生自指定的基类。在 C# 8.0 及更高版本中的可为 null 上下文中, T 必须是从指定基类派生的不可为 null 的引用类型。
  • where T : <基类名>?,类型参数必须是指定的基类或派生自指定的基类,在 C# 8.0 及更高版本中的可为 null 上下文中, T 可以是从指定基类派生的可为 null 或不可为 null 的类型。
  • where T : <接口名称>,类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。 在 C# 8.0 及更高版本中的可为 null 上下文中, T 必须是实现指定接口的不可为 null 的类型。
  • where T : <接口名称>?,类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。 在 C# 8.0 中的可为 null 上下文中, T 可以是可为 null 的引用类型、不可为 null 的引用类型或值类型。 T 不能是可为 null 的值类型。
  • where T : struct ,类型参数必须是不可为 null 的值类型。
  • where T : class? ,类型参数必须是可为 null 或不可为 null 的引用类型。
  • where T : notnull ,类型参数必须是不可为 null 的类型。
  • where T : default ,重写方法或提供显式接口实现时,如果需要指定不受约束的类型参数,此约束可解决歧义。
  • where T : unmanaged ,类型参数必须是不可为 null 的非托管类型。

后续补充中...

更新于