详解C#的协变和逆变
一、使用协变(Covariance)和逆变(Contravariance )能够实现数组之间、委托实例和方法之间、泛型委托实例之间、泛型接口的变量和泛型类型的对象之间、泛型接口的变量之间的隐式转换;使用协变将允许使用比原指定类型派生程度更大(即更具体的)的类型,使用逆变将允许使用比原指定类型派生程度更小(即更不具体的)的类型;
1.协变和逆变都只支持引用类型,不支持值类型;
2.如果泛型接口或泛型委托的类型参数被声明为协变或逆变,则该泛型接口或泛型委托被称为变体(Variant);
二、数组只支持协变,即支持派生程度更大的类型的数组隐式转换为派生程度更小的类型的数组:
object[] myArray = new string[];
IComparable[] myOtherArray = new string[];
1.此操作不是类型安全的,给上述数组添加原数组不兼容的对象时会抛出异常ArrayTypeMismatchException:
//myArray[0] = 10; //此处10会被装箱为object类型,而object类型的对象不能隐式转换为string类型
2.由于值类型不支持协变和逆变,因此下面的转换是错误的:
//object[] myArray = new int[5];
//IComparable[] myOtherArray = new int[5];
三、委托支持协变和逆变,为匹配委托类型和方法签名提供更大的灵活性,不仅可以将签名完全匹配的方法分配给委托实例,还可以通过协变将返回值类型与委托类型的返回值类型相比派生程度更大的方法分配给委托实例;通过逆变将参数类型与委托类型的参数类型相比派生程度更小的方法分配给委托实例:
public class MyBaseClass { }
public class MyClass : MyBaseClass { }
public class MyDerivedClass : MyClass { }
public delegate MyClass MyDelegate(MyClass obj);
public delegate T MyDelegate<T>(T obj);
//对于给定的方法
public staitc MyDerivedClass MyFunc(MyBaseClass obj)
{
return new MyDerivedClass();
}
//使用时:
MyDelegate myDelegate = MyFunc; //同时使用了协变和逆变
MyDelegate<MyClass> myDelegate = MyFunc;
四、在定义泛型委托时,可以通过将类型参数声明为逆变量或协变量来定义变体委托(Variant Delegate),从而使具有协变和逆变转换关系的不用类型的泛型委托实例之间进行隐式转换,使用关键字out修饰返回值的类型参数以支持协变,使用关键字in修饰参数的类型参数以支持逆变:
public delegate U MyDelegate<in T, out U>(T obj);
//使用时:
MyDelegate<MyClass,MyClass> myDelegate = MyFunc;
MyDelegate<MyBaseClass, MyDerivedClass> myOtherDelegate = MyFunc;
//隐式转换:
myDelegate = myOtherDelegate;
1.变体委托不支持合并,即Delegate.Combine方法需要委托的类型完全相同才能合并,不支持变体委托的转换;在运行时合并不同类型的变体委托会抛出异常System.ArgumentException;
五、在定义泛型接口时,可以通过将类型参数声明为逆变量或协变量来定义变体接口(Variant Interface),协变允许方法的返回值类型比接口定义中返回值类型参数的派生程度更大,逆变允许方法的参数类型比接口定义中参数类型参数的派生程度更小;使用关键字out修饰返回值的类型参数以支持协变,使用关键字in修饰参数的类型参数以支持逆变:
public interface IMyInterface<in T, out U>
{
U MyFunc();
void MyFoo(T obj);
}
public class MyClass<T, U> : IMyInterface<T, U>
{
public void MyFoo(T obj)
{
//do…
} }
//使用时:
IMyInterface<string, object> myObj = new MyClass<object, string>();
IMyInterface<object, string> myObj1 = new MyClass<object, string>();
myObj = myObj1;
1.实现变体接口的类型仍然是不可变量;
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!
作者:Minotauros
出处:https://www.cnblogs.com/minotauros/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
详解C#的协变和逆变的更多相关文章
- .Net中委托的协变和逆变详解
关于协变和逆变要从面向对象继承说起.继承关系是指子类和父类之间的关系:子类从父类继承所以子类的实例也就是父类的实例.比如说Animal是父类,Dog是从Animal继承的子类:如果一个对象的类型是Do ...
- C#4.0泛型的协变,逆变深入剖析
C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...
- C#协变和逆变
我们知道在C#中,是可以将派生类的实例赋值给基类对象的.
- C# 泛型的协变和逆变
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如 ...
- 不变(Invariant), 协变(Covarinat), 逆变(Contravariant) : 一个程序猿进化的故事
阿袁工作的第1天: 不变(Invariant), 协变(Covarinat), 逆变(Contravariant)的初次约 阿袁,早!开始工作吧. 阿袁在笔记上写下今天工作清单: 实现一个scala类 ...
- 再谈对协变和逆变的理解(Updated)
去年写过一篇博客谈了下我自己对协变和逆变的理解,现在回头看发现当时还是太过“肤浅”,根本没理解.不久前还写过一篇“黑”Java泛型的博客,猛一回头又是“肤浅”,今天学习Java泛型的时候又看到了协变和 ...
- 【转】c# 协变和逆变
本文转自:http://www.cnblogs.com/rr163/p/4047404.html C#的协变和逆变 由子类向父类方向转变是协变,用out关键字标识,由父类向子类方向转变是逆变,用in关 ...
- .NET 4.0中的泛型的协变和逆变
转自:http://www.cnblogs.com/jingzhongliumei/archive/2012/07/02/2573149.html 先做点准备工作,定义两个类:Animal类和其子类D ...
- 深入理解 C# 协变和逆变
msdn 解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型. “逆变”则是指能够使用派生程度更小的类型. 解释的很正确,大致就是这样,不过不够直白. 直白的理解: “协变” ...
随机推荐
- Eclipse Java注释模板设置
类型(Types)注释标签(类的注释): /** * @ClassName: ${type_name} * @Description: ${todo}(这里用一句话描述这个类的作用) * @autho ...
- gd_t , bd_t 结构分析
在分析板级初始化函数board_init_f 和 board_init_r 之前,先来看一下在uboot中颇为重要的 gd_t, bd_t 结构 bd_t 所对应的定义bd_info 在 arch/a ...
- HTML中关于class内容空格多类名的问题详解
之所以想谈谈这个,不明所以.所以转载下来方便自己看看. 问:像 class="info fl" 这种class定义是何意思? 答:这里的空格隔开后,它们所代表的是两个类名,分别为i ...
- jsp相关笔记(二)
在jsp中将数据库表格内容读出为一个表格,并在表格中添加超链接: <%@ page language="java" contentType="text/html; ...
- 安装配置python环境,并跑一个推荐系统的例子
1.官网下载python2.7,安装完后,在环境变量Path中加上这个路径 在控制台输入python,出现版本信息,就成功了. 2.我使用的是 pycharm,注册后,在 把自己的python.exe ...
- win7 home 提升 admin, pip
以管理员权限运行cmd cmd 右击‘以管理员身份运行’ 输入net user administrator /active:yes 取消为 net user administrator /acti ...
- PostgreSQL和MySQL
PostgreSQL分布式
- git-创建新项目
1.一般第一次使用git,需要进行全局设置,如果下次创建新项目或者fork别人的项目,则不需要再进行设置:但是如果想要提交到不同的代码管理网站,则需要再设置,比如现在我的是在gitlab.com上进行 ...
- (转载)RHEL7(RedHat 7)本地源的配置
配置yum源 1.首先连接RedHat7的DVD再把挂载DVD光盘到/mnt 因为配置时候路径名里面不能有空格,否则不能识别 [root@ mnt]# mount /dev/cdrom /mnt ...
- 2.DI依赖注入
一:DI Dependency Injection ,依赖注入 is a :是一个,继承. has a:有一个,成员变量,依赖. class B { private A a; //B类依赖A类 } ...