设为首页 加入收藏

TOP

C# 泛型的协变和逆变
2015-07-16 12:56:58 来源: 作者: 【 】 浏览:7
Tags:逆变

可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量。协变和逆变是两个相互对立的概念:


在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间拥有继承关系,简而言之,在C# 4.0之前的泛型都是不支持协变和逆变的。


我们来看一段利用了协变类型参数的代码:


下面我们利用协变类型参数,可以执行类似于普通的多态性的分配:


在上面的实例中,在C# 4.0之前是不能正常编译的,除了对赋值给基类集合时将子类集合做一个强制转换,但是在运行时仍然会抛出一个类型转换的异常。


下面我们再看一个关于逆变的实例代码:


在上面的示例中我们 Action 类型的委托分配给类型 Action 的变量,根据逆变的定义我们可以知道 Action 类型是支持逆变的。


为什么IEnumerableAction 可以分别支持类型的逆变和协变呢?我们查看这两个类型在 .NET 中的定义:


为了保证类型的安全,C#编译器对使用了 outin 关键字的泛型参数添加了一些限制:


1. 不支持类的类型参数的可变性


只有接口和委托可以拥有可变的类型参数。inout 修饰符只能用来修饰泛型接口和泛型委托。


2. 可变性只支持引用转换


可变性只能用于引用类型,禁止任何值类型和用户定义的转换,如下面的转换是无效的:


3. 类型参数使用了 out 或者 ref 将禁止可变性


对于泛型类型参数来说,如果要将该类型的实参传给使用 out 或者 ref 关键字的方法,便不允许可变性,如:


这段代码编译器会报错。


4. 可变性必须显式指定


从实现上来说编译器完全可以自己判断哪些泛型参数能够逆变和协变,但实际却没有这么做,这是因为C#的开发团队认为:


5. 注意破坏性修改


在修改已有代码接口的可变性时,会有破坏当前代码的风险。例如,如果你依赖于不允许可变性的is或as操作符的结果,运行在.NET 4时,代码的行为将有所不同。同样,在某些情况下,因为有了更多可用的选项,重载决策也会选择不同的方法。所以在对已有代码引入可变性时要做好足够的单元测试以及防御措施。


6. 多播委托与可变性不能混用


下面的代码能够通过编译,但是在运行时会抛出 ArgumentException 异常:


这是因为负责链接多个委托的 Delegate.Combine方法要求参数必须为相同的类型。上面的示例我们可以修改成如下正确的代码:


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Java中如何实现单例模式 下一篇Python进程池:multiprocessing.p..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: