泛型类型的协变(covariant)和逆变
官网:http://msdn.microsoft.com/zh-cn/library/dd799517.aspx
原文链接:http://book.51cto.com/art/201112/308570.htm
参考链接:http://www.cnblogs.com/yukaizhao/archive/2011/10/27/xiebian-nibian.html
1.3.4 泛型类型的协变(covariant)和逆变(contravariant)
在.NET 4.0之前的版本中,泛型类型是不支持协变和逆变的,但是委托类型的参数是支持协变和逆变的。什么是协变和逆变呢?在编程语言中,“协变”是指能够使用与原始指定的派生类型相比派生程度更大的类型;“逆变”则是指能够使用派生程度更小的类型。
下面的代码很好地演示了委托类型的协变。假定有一个类Animals,从其派生出一个子类Dogs,那么当定义一个委托,该委托返回Animals。用户也可以将一个返回Dogs的委托赋值给该委托,称之为协变,见代码1.4。
代码1.4 委托的协变
- class Program
- {
- public delegate Animals HandlerMethod(); //返回Animals的委托
- public static Animals FirstHandler() //返回Animals的方法实现
- {
- Console.WriteLine("返回Animals的委托");
- return null;
- }
- public static Dogs Secondhandler() //返回Dogs的方法实现
- {
- Console.WriteLine("返回Dogs的委托");
- return null;
- }
- static void Main(string[] args)
- {
- HandlerMethod handler1 = FirstHandler; //标准委托
- HandlerMethod handler2 = Secondhandler; //委托协变
- }
- }
- // 定义一个Animals的类
- public class Animals
- {
- public string Location { get; set; }
- }
- // 定义一个派生自Animals的Dogs类
- public class Dogs : Animals
- {
- public string Cry { get; set; }
- }
在上面的代码中,首先定义了Animals类和Dogs类,然后定义了一个名为HandlerMethod的委托,该委托返回Animals类型的 值。在Main()方法中,分别赋给一个返回Animals类型的值和一个返回Dogs类型值的方法。可以看到,由于委托的协变特性,使得本来返回一个 Animals的委托可以接受一个返回Dogs的委托。
.NET 4.0引入了in/out参数,使泛型类型的协变和逆变得以实现。比如定义一个泛型接口或者是泛型委托,可以使用out关键字,将泛型类型参数声明为协变。协变类型必须满足条件:类型仅用作接口方法的返回类型,不用作方法参数的类型。
可以使用in关键字,将泛型类型参数声明为逆变。逆变类型只能用作方法参数的类型,不能用作接口方法的返回类型。逆变类型还可用于泛型约束。下面的示例演示了如何使用in/out参数来设置泛型类型的协变和逆变。协变的使用见代码1.5。
代码1.5 泛型的协变
- interface ITest<out T> //定义一个支持协变的接口
- {
- T X { get; } //属性
- T M(); //返回T类型的方法
- }
- //定义一个实现接口的泛型类
- class TestClass<T> : ITest<T>
- where T : Base, new() //约束T要派生自Base,具有构造函数
- {
- public T X { get; set; }
- //实现泛型方法
- public T M()
- {
- return new T();
- }
- }
- //定义两个类
- class Base { }
- class Derived : Base { }
- class Program
- {
- static void Main(string[] args)
- {
- ITest<Derived> _derived =
- new TestClass<Derived> { X = new Derived() }; //使用对象初始化语法赋初值
- ITest<Base> _base = _derived; //泛型协变
- Base x = _base.X;
- Base m = _base.M();
- }
- }
在上面的代码中,定义了一个泛型接口ITest,注意使用了out参数以支持协变。然后TestClass泛型类实现了接口,并且定义了泛型约束指 定T类型必须是派生自Base类的子类。可以看到在Main主窗体中,定义了一个ITest的接口,然后利用泛型的协变特性来进行泛型类型之间的变换。
与协变相反的是,逆变是将基类转换为派生类,泛型逆变有如下两条规则:
泛型参数受in关键字约束,只能用于属性设置或委托(方法)参数。
隐式转换目标的泛型参数类型必须是当前类型的“继承类”。
例如,代码1.6定义了一个接口,演示了哪些是允许协变,哪些是允许逆变的。
代码1.6 接口的逆变
- interface ITest<in T>
- {
- T X
- {
- get; //获取属性不允许逆变
- set; //设置属性允许逆变!
- }
- T M(T o); //只允许方法参数,不能作用于方法返回值
- }
与协变相反,逆变符合多态性的规律,逆变有些令人费解,不过逆变主要是为泛型委托准备的。逆变的使用如代码1.7所示。
代码1.7 委托的逆变
- class Program
- {
- static void Main(string[] args)
- {
- Action<Base> _base = (o) => Console.WriteLine(o);//定义一个Base基类
- Action<Derived> _derived = _base; //使用协变将基类转换为派生类
- _derived(new Derived()); //逆变的效果
- }
- }
以上代码中创建了一个委托,是基于Base类,但是在后面的赋值语句中,将基类赋给派生类,形成了逆变。
泛型类型的协变(covariant)和逆变的更多相关文章
- 协变(covariant)和逆变(contravariant)
我们知道子类转换到父类,在C#中是能够隐式转换的.这种子类到父类的转换就是协变. 而另外一种类似于父类转向子类的变换,可以简单的理解为“逆变”. 上面对逆变的简单理解有些牵强,因为协变和逆变只能针对接 ...
- C#中的协变(Covariance)和逆变(Contravariance)
摘要 ● 协变和逆变的定义是什么?给我们带来了什么便利?如何应用? ● 对于可变的泛型接口,为什么要区分成协变的和逆变的两种?只要一种不是更方便吗? ● 为什么还有不可变的泛型接口,为什么有的泛型接口 ...
- C#中的协变OUT和逆变
泛型接口和泛型委托中经常使用可变性 in 逆变,out 协变 从 list<string>转到list<object> 称为协变 (string 从object 派生,那么 ...
- C# 协变out 、逆变 in
需求:泛型使用多态性 备注:协变逆变只能修饰 接口和委托 简单理解: 1.使用 in 修饰后为逆变,只能用作形参使用 ,参考 public delegate void Action<in T&g ...
- Scala 基础(十六):泛型、类型约束-上界(Upper Bounds)/下界(lower bounds)、视图界定(View bounds)、上下文界定(Context bounds)、协变、逆变和不变
1 泛型 1)如果我们要求函数的参数可以接受任意类型.可以使用泛型,这个类型可以代表任意的数据类型. 2)例如 List,在创建 List 时,可以传入整型.字符串.浮点数等等任意类型.那是因为 Li ...
- Kotlin泛型与协变及逆变原理剖析
在上一次https://www.cnblogs.com/webor2006/p/11234941.html中学习了数据类[data class]相关的知识,这次会学习关于泛型相关的东东,其中有关于泛型 ...
- .NET泛型03,泛型类型的转换,协变和逆变
协变(Convariant)和逆变(Contravariant)的出现,使数组.委托.泛型类型的隐式转换变得可能. 子类转换成基类,称之为协变:基类转换成子类,称之为逆变..NET4.0以来,支持了泛 ...
- 深入理解 C# 协变和逆变
msdn 解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型. “逆变”则是指能够使用派生程度更小的类型. 解释的很正确,大致就是这样,不过不够直白. 直白的理解: “协变” ...
- 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程
额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...
随机推荐
- 我在用的mac软件(3)-效率篇
距离上篇博客竟然一晃就2个月过去了……最近确实太忙了,但一忙其实就容易乱,反而积累就少. 今天继续介绍下我在用的mac软件.标题为效率篇,其实没有严格的限定,就是杂乱的介绍我在用的感觉能提升效率的工具 ...
- iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)
前言:使用NSURLSessionDownloadTask满足不这个需要离线断点续传的下载需求,所以这里就需要使用NSURLSessionDataTask的代理方法来处理下载大文件,并且实现离线断点续 ...
- Regex Expression的资料和笔记整理
维基百科:http://en.wikipedia.org/wiki/Regular_expression 正则表达式在线测试:http://tool.chinaz.com/regex/ 正则表达式,常 ...
- 团队交流合作简单解决方案:TeamViewer远程控制&会议演示 + HyperCam屏幕录制(免费)
一. 教程摘要 做开发,团队合作是少不了的.而在合作中,有一部分是花在交流讨论上,其中包括初期的任务分配,成员的进度汇报,以及资源和心得分享等.该教程介绍了两个免费的软件,搭配起来,适合人数不超过25 ...
- iptables & selinux
iptables -F getenforce setenforce 0 可以临时关闭,但重启之后还是会变成原来的状态. vi /etc/sysconfig/selinux 把里边的一行改为 SELIN ...
- Effective Java 65 Don't ignore exceptions
Principle An empty catch block defeats the purpose of exceptions, which is to force you to handle ex ...
- redis的简单安装配置
一.简介 Redis是一种高级key-value数据库,数据可以持久化,支持的数据类型很丰富,有字符串,哈希,链表,集合和有序集合5种数据类型 Redis支持在服务器端计算集合的并,交和补集(diff ...
- Java中读取properties资源文件
一.通过ResourceBundle来读取.properties文件 /** * 通过java.util.resourceBundle来解析properties文件. * @param String ...
- nginx 负载均衡示例
一.nginx nginx是一个轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,以开源形式发布.nginx的性能稳定,功能丰富,配置简单,且占用系统资源低.可支持多个 ...
- HTTP协议状态码
如果向您的服务器发出了某项请求要求显示您网站上的某个网页(例如,当用户通过浏览器访问您的网页或在检测工具抓取该网页时),那么,您的服务器会返回 HTTP 状态代码以响应该请求. 一些常见的状态代码为: ...