泛型约束
基类约束
可以访问基类的属性和方法,限制以后参数类型只能是该类型或其子类。
1 | public static void GenericMethod<T>(T t) where T :GenericClass{} |
接口约束
可以访问接口的属性和方法,限制以后参数类型必须有实现该接口。
1 | public static void GenericMethod<T>(T t) where T : IGeneric{} |
引用类型约束
限制参数类型只能是引用类型。
1 | public static void GenericMethod<T>(T t) where T : class{} |
值类型约束
限制参数类型只能是值类型。
1 | public static void GenericMethod<T>(T t) where T : struct{} |
无参数构造函数约束
限制参数类型可以被无参数构造函数实例化。
1 | public static void GenericMethod<T>(T t) where T : new(){} |
注:以上可以根据需求多重约束,叠加使用,其是“且”的关系。
1 | public static void GenericMethod<T>(T t) where T : GenericClass,IGeneric{} |
协变与逆变
使用场景
逆变(contravariant)与协变(covariant)是C#4新增的概念,在此之前泛型的参数是不能变化的,无论是“逆”还是“顺”(协)。
而在泛型参数上添加了in关键字作为泛型修饰符的话,那么那个泛型参数就只能用作方法的输入参数,或者只写属性的参数,不能作为方法返回值等,总之就是只能是“入”,不能出。out关键字反之。
- 协变(covariant):out 修饰返回值。例如
Func<out T>
- 逆变(contravariant):in 修饰传入参数。例如
Action<in T>
应用
1 | public class Bird{} |
1 | { |
总结
逆变(in) 英语单词字面理解就是入参,而中文的字面意思就是逆向的即从子向父转换,可以参考系统提供的委托 Action<T>
。
Action<T>
委托这样使用可以理解为一个已经限制了入参为派生类的委托,那么这个委托将不能赋值给入参为基类型的委托。也就是说如果已经限制了一个委托的入参类型,那么这个委托的入参只能是该类型或该类型的派生类型,否则会影响委托内方法、属性等的调用。所以反之我们可以将一个入参为基类型的委托赋值给一个入参是派生类型的委托。
1 | // public delegate void Action<in T>(T obj); |
协变(out) 英语单词字面理解就是出参,而中文的字面意思就是正向(协有顺的意思)的即从父向子转换,可以参考系统提供的委托 Func<T>
。
Func<T>
委托这样使用可以理解为一个已经限制返回值为基类型的委托,那么这个委托将不能赋值给返回值为派生类的委托。也就是说已经限制了一个委托的返回值类型,那么这个委托的返回值只能是该类型或该类型的基类型,因为我们并不能确定该返回值的类型是否是该派生类型。所以反之我们可以将返回值为派生类的委托赋值给一个返回值为基类型的委托。
1 | // public delegate TResult Func<out TResult>(); |
如果感觉协变和逆变的内容难以理解,可以参看博客园的文章逆变与协变详解,实际我们只需要了解基本概念以及学会如何使用即可,对于内部的实现原理不必深究。
泛型缓存
因为每一个泛型类型,都会生成不同副本,适合不同类型,需要缓存一份数据的场景。而无论是线程安全性还是检索速度,泛型缓存都要比字典类型表现更佳。
1 | public class GenericCache<T> |