深入理解 C# 协变和逆变

msdn 解释如下:

协变是指能够使用与原始指定的派生类型相比,派生程度更大的类型。

逆变则是指能够使用派生程度更小的类型。

 

解释的很正确,大致就是这样,不过不够直白。

直白的理解:

协变”->”和谐的变”->”很自然的变化”->string->object :协变。

逆变”->”逆常的变”->”不正常的变化”->object->string 逆变。

 

上面是个人对协变和逆变的理解,比起记住那些派生,类型,原始指定,更大,更小之类的词语,个人认为要容易点。

 

下面是一则笑话:

一个星期的每一天应该这样念:

星期一 = 忙day; 
星期二 = 求死day; 
星期三 = 未死day; 
星期四 = 受死day; 
星期五 = 福来day; 
星期六 = 洒脱day; 
星期天 = 伤day

 

为了演示协变和逆变,以及之间的区别,请创建控制台程序CAStudy,手动添加两个类:

image

因为是演示,所以都是个空类,

只是有一点记住Dog 继承自Animal,

所以Dog变成Animal 就是和谐的变化(协变),而如果Animal 变成Dog就是不正常的变化(逆变)

 

Main函数中输入:

image

 

因为Dog继承自Animal,所以Animal aAnimal = aDog; aDog 会隐式的转变为Animal.

但是List<Dog> 不继承List<Animal> 所以出现下面的提示:

image

 

如果想要转换的话,应该使用下面的代码:

List<Animal> lstAnimal2 = lstDogs.Select(d => (Animal)d).ToList();

 

可以看到一个lstDogs 变成lstAnimal 是多么复杂的操作了。

正因如此,所以微软新增了两个关键字:Out,In,下面是他们的msdn解释:

image

image

 

协变的英文是:“covariant”,逆变的英文是:“Contravariant

为什么Microsoft选择的是”Out” ”In” 作为特性而不是它们呢?

 

我个人的理解:

因为协变和逆变的英文太复杂了,并没有体现协变和逆变的不同,但是out  in 却很直白。

out: 输出(作为结果),in:输入(作为参数)

所以如果有一个泛型参数标记为out,则代表它是用来输出的,只能作为结果返回,而如果有一个泛型参数标记为in,则代表它是用来输入的,也就是它只能作为参数。

 

目前out in 关键字只能在接口和委托中使用,微软使用out  in 标记的接口和委托大致如下:

image

image

先看下第一个IEnumerable<T>

image 

 

和刚开始说的一样,out 标记,所以T代表了输出,也就是只能作为结果返回。

public static void Main()

{

    Dog aDog = new Dog();

    Animal aAnimal = aDog;

 

    List<Dog> lstDogs = new List<Dog>();

    //List<Animal> lstAnimal = lstDogs;

    List<Animal> lstAnimal2 = lstDogs.Select(d => (Animal)d).ToList();

 

    IEnumerable<Dog> someDogs = new List<Dog>();

    IEnumerable<Animal> someAnimals = someDogs;

}

 

因为T只能做结果返回,所以T不会被修改, 编译器就可以推断下面的语句强制转换合法,所以

IEnumerable<Animal> someAnimals = someDogs;

可以通过编译器的检查,反编译代码如下:

image

 

虽然通过了C#编译器的检查,但是il 并不知道协变和逆变,还是得乖乖的强制转换。

在这里我看到了这句话:

IEnumerable<Animal> enumerable2 = (IEnumerable<Animal>) enumerable1;

那么是不是可以List<Animal> lstAnimal3 = (List<Animal>)lstDogs; 呢?

想要回答这个问题需要在回头看看Clr via C# 关于泛型和接口的章节了,我就不解释了,

答案是不可以。

 

上面演示的是协变,接下来要演示下逆变。

为了演示逆变,那么就要找个in标记的接口或者委托了,最简单的就是:

clip_image002  

 

Main函数中添加:

Action<Animal> actionAnimal = new Action<Animal>(a => {/*让动物叫*/ });

Action<Dog> actionDog = actionAnimal;

actionDog(aDog);

 

很明显actionAnimal 是让动物叫,因为DogAnimal,那么既然Animal 都能叫,Dog肯定也能叫。

 

In 关键字:逆变,代表输入,代表着只能被使用,不能作为返回值,所以C#编译器可以根据in关键字推断这个泛型类型只能被使用,所以Action<Dog> actionDog = actionAnimal;可以通过编译器的检查。

 

再次演示Out关键字:

添加两个类:

public interface IMyList<out T>

{

    T GetElement();

}

 

public class MyList<T> : IMyList<T>

{

    public T GetElement()

    {

        return default(T);

    }

}

 

因为out 关键字,所以下面的代码可以通过编译

IMyList<Dog> myDogs = new MyList<Dog>();

IMyList<Animal> myAnimals = myDogs;

 

将上面的两个类修改为:

public interface IMyList<out T>

{

    T GetElement();

    void ChangeT(T t);

}

 

public class MyList<T> : IMyList<T>

{

    public T GetElement()

    {

        return default(T);

    }

 

    public void ChangeT(T t)

    {

        //Change T

    }

}

 

编译:

image 

因为Tout修饰,所以T只能作为参数。

 

同样修改两个类如下:

public interface IMyList<in T>

{

    T GetElement();

    void ChangeT(T t);

}

 

public class MyList<T> : IMyList<T>

{

    public T GetElement()

    {

        return default(T);

    }

 

    public void ChangeT(T t)

    {

        //Change T

    }

}

 

这一次使用in关键字。

编译:

image 

 

因为用in关键字标记,所以T只能被使用,不能作为返回值。

 

最后修改代码为:

public interface IMyList<in T>

{

    void ChangeT(T t);

}

 

public class MyList<T> : IMyList<T>

{

    public void ChangeT(T t)

    {

        //Change T

    }

}

 

编译成功,因为in代表了逆变,所以

IMyList<Animal> myAnimals = new MyList<Animal>();

IMyList<Dog> myDogs = myAnimals;

 

可以编译成功!。






本文转自LoveJenny博客园博客,原文链接:http://www.cnblogs.com/LoveJenny/archive/2012/03/13/2392747.html,如需转载请自行联系原作者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/454563.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为mate20能用鸿蒙吗,华为mate20可以用5g网络吗

华为mate20不可以用5g网络&#xff0c;它是4g手机在2018年上市&#xff0c;当时5g并没有开始流行&#xff0c;因此mate20是不支持5G的。不过在后来的2019年秋季&#xff0c;华为发布了mate20 x的5g版本&#xff0c;这也是mate20系列里唯一支持5G的&#xff0c;除此之外mate20、…

基本农田卫星地图查询_#重庆朝天门#谷歌百度腾讯高德“卫星地图”PK,谷歌更胜一筹...

截图自便民查询网&#xff0c;各家卫星地图PK&#xff0c;各有千秋~谷歌精确度最高&#xff1a;5m&#xff0c;来福士修建中&#xff0c;嘉陵江是绿的&#xff0c;长江是黄的。两江交汇处有一条分明的界线。谷歌 5m:20ft谷歌 50m:100ft谷歌 200m:500ft谷歌 300m:1000ft谷歌 500…

Python 第三方库之 Celery 分布式任务队列

一、Celery介绍和使用&#xff1a; Celery 是一个 基于python开发的分布式异步消息任务队列&#xff0c;通过它可以轻松的实现任务的异步处理&#xff0c; 如果你的业务场景中需要用到异步任务&#xff0c;就可以考虑使用celery&#xff0c; 举几个实例场景中可用的例子: 你想…

windows server 2008 (五)web服务器的搭建和部署

Windows server 2008 web服务器的搭建和部署相对于windows server 2003的IIS6来说&#xff0c;windows server 2008推出的IIS7.0为管理员提供了统一的web平台&#xff0c;为管理员和开发人员提供了一个一致的web解决方案。并针对安全方面做了改进&#xff0c;可以减少利用自定义…

改装摩托车

摩托车发动机就是将进入气缸中的燃料混合气点燃使其燃烧所产生的热能变为机械能&#xff0c;并由曲轴将动力通过传动机构传给摩托车后轮而变为车辆行驶动力的机械。发动机的进排气量和气流速是影响高转速&#xff08;功率&#xff09;输出的关键因素之一。 发动机工作时气流的路…

华为鸿蒙os logo,华为鸿蒙OS Logo曝光:Powered by HarmonyOS

IT之家 9 月 13 日消息 9 月 10 日&#xff0c;鸿蒙 OS 2.0 亮相华为开发者大会的主舞台上&#xff0c;华为常务董事、消费者业务 CEO 余承东表示&#xff0c;鸿蒙 OS 是首个真正为全场景时代打造的分布式操作系统&#xff0c;鸿蒙 OS 2.0 全面使能全场景生态。现在博主 勇气数…

python判断语句_详解Python判断语句的使用方法

本篇介绍Python判断语句的使用&#xff0c;主要讨论简单条件语句、多重条件语句和嵌套条件语句&#xff0c;在讲解的每个案例中都配有流程图和代码说明。通过本篇的学习&#xff0c;可以达成如下目标。 ● 掌握判断语句的使用规则 ● 判断语句流程图的画法 前面我们学习了Pytho…

js setTimeout 使用方法

在项目过程中遇到一些异步加载和其他js方法冲突的问题&#xff1a; 如图初始化的时候会加载“商户基本信息”,修改商户名称字段第二个页面也需要修改&#xff1a; function setSeqAndName(){var pritab2 $("#allTabs").tabs("getTab", 1).find("ifra…

机器学习算法之 logistic、Softmax 回归

逻辑回归本质是分类问题&#xff0c;而且是二分类问题&#xff0c;不属于回归&#xff0c;但是为什么又叫回归呢。我们可以这样理解&#xff0c;逻辑回归就是用回归的办法来做分类。它是在线性回归的基础上&#xff0c;通过Sigmoid函数进行了非线性转换&#xff0c;从而具有更强…

程序员成功之路

程序员成功之路 ——The road ahead for programmer&#xff08;演讲稿&#xff09; 一、我很羡慕在座的各位同学&#xff0c;因为你们是中国未来的程序员&#xff0c;而我不是&#xff0c;我一直很遗憾。 比尔盖茨曾经写过一本书叫做《未来之路》The road ahead, 那么今天我选…

部署egg需要用到pm2吗_使用宝塔面板部署校园综合服务平台项目

本文档为校园综合服务平台服务端的安装部署教程&#xff0c;欢迎star小程序端下载地址&#xff1a;https://github.com/landalfYao/help.git后台服务端下载地址&#xff1a;https://github.com/landalfYao/helpserver.git后台客户端下载地址&#xff1a;https://github.com/lan…

机器学习算法之线性回归

一、什么是回归算法 回归算法是一种有监督算法 回归算法是一种比较常用的机器学习算法&#xff0c;用来建立“解释”变量(自变量X)和观测值(因变量Y)之间的关系&#xff1b;从机器学习的角度来讲&#xff0c;用于构建一个算法模型(函数)来做属性(X)与标签(Y)之间的映射关系&a…

Console-算法[for]-国王与老人的六十四格

ylbtech-Arithmetic:Console-算法[for]-国王与老人的六十四格1.A&#xff0c;案例-- -- ylb&#xff1a;算法-- Type:算法[for]-- munu:国王与老人的六十四格-- 20:32 2012/3/16-- 案例&#xff1a;印度有个国王&#xff0c;他拥有超人的权力和巨大的财富。但权力和财富最终让他…

在OOW2009上寻宝撞大运续(床上篇)

历时5天的Oracle Open World 2009终于&#xff0c;终于结束了。今天最后的节目是去听一场金融分析师的会议&#xff0c;“只”开了不到6个钟。去的时候是毛毛雨&#xff0c;回来的时候终于看到了一缕阳光。说夕阳无限好不大合适。用Larry Ellison的说法是“太阳落下的地方也是太…

特征图注意力_从数据结构到算法:图网络方法初探

作者 | 朱梓豪来源 | 机器之心原文 | 从数据结构到算法&#xff1a;图网络方法初探如果说 2019 年机器学习领域什么方向最火&#xff0c;那么必然有图神经网络的一席之地。其实早在很多年前&#xff0c;图神经网络就以图嵌入、图表示学习、网络嵌入等别名呈现出来&#xff0c;其…

FFMPEG 源码分析

FFMPEG基本概念&#xff1a; ffmpeg是一个开源的编解码框架&#xff0c;它提供了一个音视频录制&#xff0c;解码和编码库。FFMPEG是在linux下开发的&#xff0c;但也有windows下的编译版本。 ffmpeg项目由以下几部分组成: ffmpeg 视频文件转换命令行工具,也支持经过实时电视…

面试之 Redis汇总

简介 Redis 持久化机制 RDB&#xff08;Redis DataBase&#xff09; AOF&#xff08;Append-only file&#xff09; Redis 4.0 对于持久化机制的优化 补充&#xff1a;AOF 重写 二者的区别 二者优缺点 Memcache与Redis的区别都有哪些&#xff1f; 缓存雪崩、缓存穿透、…

Oracle 10g 问题集锦

监听服务中Oracle数据库之中使用最主要的一个服务&#xff0c;但是这个服务经常会出现错误&#xff0c;包括以后在工作之中此服务也会出现错误&#xff0c;故给出两种常见错误的解决方案&#xff08;故障1、故障2&#xff09; 故障1: 注册表使用了优化软件&#xff08;如&#…

iOS linker command failed with exit code 1 (use -v to see invocation)多种解决方案汇总

有时可能会遇到这种错误&#xff0c;关键是这种错误&#xff0c;有时只有这一句话&#xff0c;也不会给更多错误信息。 网上找了一些&#xff0c;总结了如下&#xff1a;&#xff08;PS&#xff1a;以下是按照解决简易程度排序&#xff0c;不代表出现概率&#xff09; 1、bitco…

面试之 Mysql 汇总

事务相关 什么是事务&#xff1f; 事务&#xff1a;是由一组SQL语句组成的逻辑处理单元&#xff0c;事务具有以下4个属性&#xff0c;通常简称为事务。事务的ACID属性&#xff1a; &#xff08;1&#xff09;原子性&#xff08;Atomicity&#xff09;&#xff1a;   事务是一…