Covariance In .Net(ZT)
Problem:
我们可能会遇到这样的事情,就是在一些使用了泛型的接口或者数组中,即使只有泛型参数的类型不同,这些类型也不能像普通类一样互相转换
Eg.
class Animal {}
class Bear : Animal {}
class Camel : Animal {}
...
Stack<Bear> bears = new Stack<Bear>();
Stack<Animal> animals = bears; // Compile error
Bear b=new Animal(); // no compile error
.....
什么是 Covariance
Covariance 用于数组、泛型接口和泛型委托中,如果存在泛型接口 I<T> 的两个实例 I<A> 和 I<B>,而 A 和 B 之间有继承关系(A is base, B is superclass),且 A > B。如果允许 I<B> 隐式转换为 I<A>,则成 I<T> 支持 Covariance。
C#数组的协变(Array Covariance) s supported from .net Framework 1.0
Eg.
object[] array=new string[10];// no compile error
but it is not type safe
array[0]=1; // will get run-time error
From .Net 4
如果存在类 A 和 B,A 是 B 的基类(A > B),那么 I<T> 是一个泛型接口,T 约束为 A(where T : A),那么从类型 I<B> 可以转换为类型 I<A>。
对于委托,如果存在类 A 和 B,A 是 B 的基类(A > B),那么 D<T>() 是一个泛型委托,T 约束为 A(where T : A),那么从委托 D<B> 可以转换为委托 D<A>。
如何声明一个接口或者委托支持 Covariance
C# 4.0 现在提供一个新的约束性语法,在声明返型接口或委托时,可以在泛型参数前面加上 in 或者 out 来表示该接口或委托支持 Contra-variance 或 Covariance。这篇文章只涉及到 Covariance,因此我们需要一个 out 约束。
某一些 CLR 系统内置的类,如 IEnumerable<T>,已经被声明成了 IEnumerable<out T>。我们通过 Metadata 看到的 System.Collections.Generic.IEnumerable<T> 如下。
这就意味着 .NET Framework 4.0 中的 IEnumerable<T> 支持 Covariance。于是,下面的代码是可以工作的 。
我们也可以自己实现 Covariance。如下面的例子,类 A 和 类 B 具备继承关系,下面的转换都是合法的。因为 object > A > B,因此 B –> A,B –> object 以及 A –> object 均有效。
按照惯例,不是所有具备泛型参数的接口和委托都可以用来支持 Covariance。如果类型 I<T> 或委托 D<T> 的类型参数 T 具备 out 约束,那么:
T 是类、结构或者枚举类型。
T 是非泛型接口或者委托类型。
T 是元素类型支持 Variance 的数组。
T 是一个没有被定义为 out 约束的类型参数类型。
另外,支持 Covariance (有 out 约束)的泛型接口或委托存在以下限制:
他们不能包含一个具备类型参数表的事件(但自定义事件或者使用委托声明的事件除外)。
他们不能包含内部类(Nested Class)、结构或枚举类型。
其他需要注意的地方
作为最佳实践,泛型接口的类型参数 T 一般为 out 约束(Covariance),泛型委托的类型参数如果是作为参数列表的,则一般为 in 约束(Contra-variance),而类型参数作为返回值的,一般为 out 约束(Covariance)。关于这一点请参照 IEnumerable<out T> 和 Func<in T, out TResult>。
0 Comments:
Post a Comment
<< Home