协变(covariant)和逆变(contravariant)
我们知道子类转换到父类,在C#中是能够隐式转换的。这种子类到父类的转换就是协变。
而另外一种类似于父类转向子类的变换,可以简单的理解为“逆变”。
上面对逆变的简单理解有些牵强,因为协变和逆变只能针对接口和代理类型。而父类和子类之间不存在这种逆变的概念。
协变和逆变的本质都是子类安全的转到父类的过程。
下面就来加深下印象,先定义两个类Car和Baoma
public class Car
{
} public class Baoma : Car
{
}
明显Baoma(宝马)是Car的子类
1,先来看看协变
协变在C#中要用out关键字标明,用这个关键字就表示参数T只能用于函数,属性等的返回值。
//协变(covariant)
public interface ICo<out T>
{
T Test1();
}
//协变(covariant)
ICo<Car> ico1 = null;
ICo<Baoma> ico2 = null;
ico1 = ico2;//子类-->父类
Car car = ico1.Test1();//实际调用ico2.Test1()返回Baoma类型,当然可以赋值给父类Car
//ico2 = ico1;//error
//Baoma baoma = ico2.Test1();//实际调用ico1.Test1()返回Car类型,赋值给子类Baoma,显然不能保证类型安全
2,在来看看逆变
逆变在C#中用in关键字标明,表明参数T只能用于函数的参数。
//逆变(contravariant)
public interface IContra<in T>
{
void Test1(T t);
}
//逆变(contravariant)
IContra<Car> icontra1 = null;
IContra<Baoma> icontral2 = null;
//icontra1 = icontral2;//error
//icontra1.Test(new Car()); 实际调用IContra<Baoma>.Test(Baoma),参数Car不能安全的转换为Baoma
icontral2 = icontra1;//看似很奇怪,但这里不是Car->Baoma,而是IContra<Car>->IContra<Baoma>
icontral2.Test1(new Baoma());//实际调用IContra<Car>.Test(Car),参数Baoma能安全的转换为Car
上面的代码中已经标注详细了调用的过程,对于理解很有帮助。下面分析两种复杂一点的过程。
3,带有逆变或协变的接口作为函数参数
public interface IFoo<in T>
{
void Test(T t);
}
public interface IBar<out T>//这里T必须用out,而不是in
{
void Test(IFoo<T> foo);
}
IFoo<Car> ifoo1 = null;
IBar<Car> ibar1 = null;
IBar<Baoma> ibar2 = null;
ibar1 = ibar2;//协变
ibar1.Test(ifoo1);//实际调用IBar<Baoma>的Test(IFoo<Baoma>),
//要求IFoo<Car>要转换到IFoo<Baoma>,这就需要IFoo对T逆变,即用in关键字
引用装配脑袋对这种情况的归纳:
//如果一个接口需要对T协变,那么这个接口所有方法的参数类型必须支持对T的反变。
//同理我们也可以看出,如果接口要支持对T反变,那么接口中方法的参数类型都必须支持对T协变才行。
//这就是方法参数的协变-反变互换原则。
4,带有逆变或协变的接口作为函数的返回值
public interface IFoo<in T>
{
void Test(T t);
}
public interface IBar2<in T>
{
IFoo<T> Test();
}
IBar2<Car> a = null;
IBar2<Baoma> b = null;
b = a;//逆变
IFoo<Baoma> ibaoma = b.Test();//实际调用IBar2<Car>.Test返回IFoo<Car>类型,IFoo<Car>要转换成IFoo<Baoma>
//需要IFoo对T逆变,这种函数返回值的转换方向是一致的。
引用装配脑袋对这种情况的归纳:
//如果一个接口需要对T进行协变或反变,那么这个接口所有方法的返回值类型必须支持对T同样方向的协变或反变。
//这就是方法返回值的协变-反变一致原则。
以上仅仅是个人理解归纳,关于这个概念的理解,可以参看园子里的这两篇文章,写的非常详细。
装配脑袋的:
http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html
还有这篇,用图的方式很好理解:
http://www.cnblogs.com/lemontea/archive/2013/02/17/2915065.html
协变(covariant)和逆变(contravariant)的更多相关文章
- 逆变(contravariant)与协变(covariant):只能用在接口和委托上面
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 不变(Invariant), 协变(Covarinat), 逆变(Contravariant) : 一个程序猿进化的故事
阿袁工作的第1天: 不变(Invariant), 协变(Covarinat), 逆变(Contravariant)的初次约 阿袁,早!开始工作吧. 阿袁在笔记上写下今天工作清单: 实现一个scala类 ...
- 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 ...
- .NET泛型03,泛型类型的转换,协变和逆变
协变(Convariant)和逆变(Contravariant)的出现,使数组.委托.泛型类型的隐式转换变得可能. 子类转换成基类,称之为协变:基类转换成子类,称之为逆变..NET4.0以来,支持了泛 ...
- Kotlin泛型与协变及逆变原理剖析
在上一次https://www.cnblogs.com/webor2006/p/11234941.html中学习了数据类[data class]相关的知识,这次会学习关于泛型相关的东东,其中有关于泛型 ...
- 泛型类型的协变(covariant)和逆变
官网:http://msdn.microsoft.com/zh-cn/library/dd799517.aspx 原文链接:http://book.51cto.com/art/201112/30857 ...
随机推荐
- 一个Demo带你彻底掌握View的滑动冲突
本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 近期在又一次学习Android自己定义View这一块的内容.遇到了平时开发中常常碰到的一个棘手问题:View的滑 ...
- MySQL数据库知识点整理 (持续更新中)
一.修改用户密码 格式(在命令行下输入):mysqladmin -u 用户名 -p旧密码 password 新密码 1. 给root添加密码ab12: mysqladmin -uroot -pass ...
- 转:zTree树控件入门之checkbox:如何动态设置节点的checkbox选择框启用与禁用状态(chkDisabled)
当一棵树的部分节点根据登入用户角色不同而决定是否启用节点前的checkbox选择框的时候,我们应该如何做呢?也或者如何在页面加载的时候动态根据当前登入用户角色动态切换节点前的checkbox的禁用状态 ...
- Win 10服务智能化有什么依据?
在今年7月29日之后.Win 10操作系统就叫"视窗服务"了,当中有没有"智能'因素呢?能不能叫做"智能操作系统"呢?为什么?什么是& ...
- react-navigation使用技巧
因为react-navigation之前存在的问题相对较多,本文更新会稍慢,而且,我现在项目使用的是基于它封装的react-native-router-fluxV4版本,现在也推荐给大家使用.在下 ...
- 优化基于FPGA的深度卷积神经网络的加速器设计
英文论文链接:http://cadlab.cs.ucla.edu/~cong/slides/fpga2015_chen.pdf 翻译:卜居 转载请注明出处:http://blog.csdn.net/k ...
- [vt][xen]xenserver初始安装增加第二块硬盘&xen图形界面安装vm&设置xen里vm开机启动
为XenServer挂载/增加第二块硬盘的方法 注意: xen6.x和xen7.x vm导出导入是不兼容的.这点有点蛋疼 新买机器后安装xenserver(一般都买刻录机+dvd来刻录xen官网的io ...
- android 去掉标题栏、状态栏、横屏
// 去掉标题栏 supportRequestWindowFeature(Window.FEATURE_NO_TITLE); // 全屏.隐藏状态栏 getWindow().setFlags(Wind ...
- MediaType是application/x-www-form-urlencoded的接口测试方法
先看接口: @POST @Path("/deleteById") //@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaTy ...
- LVS之NAT模型、DR模型配置和持久连接
前言:继LVS概述,本篇实现NAT模型和DR模型下的负载均衡. NAT模型: LVS-NAT基于cisco的LocalDirector.VS/NAT不需要在RealServer上做任何设置,其只要能提 ...