这个问题是由于 C# 的早期版本,如 C# 3.0,不支持基于泛型的协变和逆变 (co- and contravariance)。比如,
class A { }
class B : A { }
List<B> bs = new List<B>();
List<A> as = bs; // 编译器错误,无法转换 List<B> 到 List<A>。
但事实上,B 是 A 的子类,也就是 B 的实例一定是 A 的实例。但由于早期的 C# 版本对于泛型的实现是通过经典的开放类型到封闭类型的模式,导致每一个泛型的封闭类型 (如 List<A>) 是一个单独的运行时类型,它产生后,没有任何办法验证泛型参数的继承性,因此,List<A> 和 List<B> 是两个完全不相干的运行时类型,他们也就理所当然的不能转换了。
从 C# 4.0 开始,泛型开始支持可变性 (Variance),可变性分为协变 (covariance) 和逆变 (contravariance)。协变是指对于两个泛型封闭类型 T<A> 和 T<B>,如果 A 是 B 的父类,那么从 T<B> 到 T<A> 的转换何以被支持;反之,逆变使得 T<A> 到 T<B> 的转换被支持。
例如,.NET 4.0 中的 IEnumerable<T> 的声明就是 IEnumerable<out T>,out 表示接下来的泛型参数 T 支持协变。所以,下面的代码:
IEnumerable<A> as = new List<A>();
IEnumerable<B> bs = new List<B>();
as = bs;
是可以被编译的。因为 List<T> 实现了 IEnumerable<out T>。
注意,协变和逆变仅适用于接口、委托和某些类型 (如数组和 Lambda 表达式),泛型参数也仅支持引用类型,对于值类型,由于其不可被继承,所以不支持可变性。
希望我的解释对您有帮助。
联系客服