这一次,终于弄懂了协变和逆变

一、前言

刘大胖决定向他的师傅灯笼法师请教什么是协变和逆变。

刘大胖:师傅,最近我在学习泛型接口的时候看到了协变和逆变,翻了很多资料,可还是不能完全弄懂。

灯笼法师:阿胖,你不要被这些概念弄混,编译器可不知道你说的什么协变逆变。这个问题,首先你得弄懂什么叫类型的可变性。

刘大胖:可变性?

二、可变性

灯笼法师:对,可变性是以一种类型安全的方式,将一个对象作为另一对象来引用。虽然是可变,但其实对象的引用地址是不会变的,只是忽悠下编译器。

刘大胖:师傅说的将一个对象作为另一对象来引用?这不就是继承么?

灯笼法师:是的,你可以看下面代码演示(C#):

刘大胖:哦,我理解了,由于MemoryStream继承于Stream,所以MemoryStream的对象可以变为Stream的对象,原来我天天在接触可变性,我竟然不知道。

灯笼法师:是的,这种转变其实遵守了里氏替换原则,爱徒,你可还记得?

刘大胖:当然,为了面试早已烂熟于心。里氏替换原则(LSP):指的是所有引用基类的地方都可以使用其子类的对象。可是师傅,这个和协变逆变有什么关系呢?

三、协变

灯笼法师:协变和逆变只是可变性的分类,主要用于泛型接口和委托中。协变逆变只是类型转换的方向不同。我们先看下接口协变吧,假如有Apple类继承于Fruit,如下:

灯笼法师:然后现在写了一个打印水果名称的方法,如下:

灯笼法师:这时如果你打算打印一些苹果的名称,你会怎么写?

刘大胖:这不是很简单,Apple继承自Fruit,那可以直接使用PrintFruit类了。撸了下,怎么报错了?代码如下:

灯笼法师:大胖,你要理清楚,虽然Apple继承Fruit,但List<Apple>和List<Fruit>却一点关系也没有,如图:

刘大胖:那如果这样,岂不是要为每一种水果都要定义一个PrintFruit方法,我觉得官方不会不知道这个问题吧?

灯笼法师:这种问题,官方当然知道了,所以才有了泛型接口的协变用以支持List<Apple>自动转为List<Fruit>。C#中使用out表示泛型参数的可协变性,List没有out约束,所以不能协变,但它的基类IEnumable却实现了,如图:

灯笼法师:所以只要把PrintFruit的参数类型换成IEnumable就可以了,如图:

刘大胖:那为什么List<T>不能加out以支持协变呢?

灯笼法师:爱徒问的好,List继承于IEnumable,它比IEnumable更宽泛,它支持读和写,但协变只能可读,主要用于约束输出参数。

刘大胖:好吧,我回去再消化下。师傅你再讲一下什么是逆变吧。

四、逆变

灯笼法师:逆变是相反的,即支持List<Fruit>转为List<Apple>,泛型接口上添加in约束输入参数。

刘大胖:有点懞,师傅你还是用代码吧!

灯笼法师:好吧,假如现在我要让苹果列表或桔子列表可以按名称排序,需要一个定义一个水果比较器,此比较器能用于任何种类的水果列表,代码如下:

灯笼法师:现在给苹果和桔子列表按名称排序吧,代码如下:

刘大胖:师傅你别忽悠我,Sort的参数可是要具体类型的比较器的,你看代码:

灯笼法师:大胖,就这是逆变,以使得基类的泛型对象替代子类的泛型对象,主要是因为IComparer<T>中使用了in关键字来约束,代码如下:

五、总结

刘大胖:哦,我有点明白了,协变就是支持泛型子类自动转泛型父类,逆变就是支持泛型父类自动转泛型子类。

灯笼法师:也可以这么理解,但这些转换只是针对编译器,其引用地址并没有改变。

翻外篇1:

协变:String =>Object

逆变:Object => String

翻外篇2:

灯笼法师在刘大胖走后从背后拿出手机,屏幕上显示来不及关闭的知乎APP:

把复杂的技术简单的写出来,更多文章请关注我的公众号:

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

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

相关文章

大一计算机专业学期计划范文,大一学习计划范文4篇

大一学习计划范文4篇时间的脚步是无声的&#xff0c;它在不经意间流逝&#xff0c;我们又将迎来新的学习任务&#xff0c;写一份学习计划&#xff0c;为接下来的学习做准备吧&#xff01;但是学习计划要写什么内容才是正确的呢&#xff1f;以下是小编精心整理的大一学习计划范文…

C++ class实现双向循环链表(完整代码)

写Delete_all的时候注意一下就好了&#xff0c;先判断空白头结点是不是为NULL 代码如下: #include <iostream> using namespace std; typedef int ElemType; #define NO 0class Node {friend class LinkList;public:Node(): next(NULL), prior(NULL) {};private:ElemTy…

5分钟看懂微服务架构下的Consul 特性及搭建

一、前言虽然说牛逼的公司都有那么几个牛逼的运维团队&#xff0c;牛逼的运维团队都有着神秘黑科技般敲代码的姿势&#xff1b;本人虽然不是一个运维工程师&#xff0c;但是自己比较爱倒腾这些东西&#xff0c;会那么一点点运维姿势&#xff0c;虽然不算专业&#xff0c;但是还…

win7 计算机定时关机脚本,win7定时关机设置及命令

电脑定时关机是一个常用的功能&#xff0c;定时关机的小软件也有不少&#xff0c;但 Win7 也为我们提供了定时关机的功能&#xff0c;额外下载软件不如就地取材。win7定时关机可通过两种方式实现&#xff0c;一种是创建计划任务&#xff0c;另一种是用win7定时关机命令 shutdow…

ASP.NET Core分布式项目实战(客户端集成IdentityServer)--学习笔记

任务9&#xff1a;客户端集成IdentityServer新建 API 项目dotnet new webapi --name ClientCredentialApi控制器添加验证using Microsoft.AspNetCore.Authorization;namespace ClientCredentialApi.Controllers {[ApiController][Route("[controller]")][Authorize]p…

C++ class实现顺序栈(完整代码)

代码如下: #include <iostream> using namespace std; typedef int ElemType;class SeqStack {public:SeqStack(int stacksize 100) {base new ElemType[stacksize];top base;size stacksize;};~SeqStack() {delete[] base;top NULL;base NULL;};int Empty_Stack(…

EF Core 数据变更自动审计设计

EF Core 数据变更自动审计设计Intro有的时候我们需要知道每个数据表的变更记录以便做一些数据审计&#xff0c;数据恢复以及数据同步等之类的事情&#xff0c; EF 自带了对象追踪&#xff0c;使得我们可以很方便的做一些审计工作&#xff0c;每次变更发生了什么变化都变得很清晰…

C++ class实现链栈(完整代码)

代码如下: #include <iostream> using namespace std; typedef int ElemType;class StackNode {friend class LinkStack;private:ElemType data;StackNode *next;StackNode(): next(NULL) {}; };class LinkStack {public:LinkStack(): top(NULL) {};~LinkStack() {Stack…

海外服务器维护,海外服务器运行不正常的原因是什么?

要开开展海外贸易业务&#xff0c;您需要选择一个由于客户操作错误&#xff0c;服务器无法正常工作。实际上&#xff0c;在使用服务器时&#xff0c;许多小型企业客户不需要定期维护服务器&#xff0c;只需要建立一个对服务器技能有一点了解的人&#xff0c;而他们需要运行服务…

使用Docker-Compose搭建高可用redis哨兵集群

头脑风暴出于学习目的&#xff0c;您可以很轻松地在docker环境下运行redis的单个实例&#xff0c;但是如果您需要在生产环境中运行它&#xff0c;那么必须将Redis部署为HA(High Avaliable)模式。Redis Sentinel为Redis提供高可用性&#xff0c;这意味着使用Sentinel可以创建Red…

C++ class实现顺序队列(完整代码)

代码如下: #include <iostream> using namespace std; typedef int ElemType;class SeqQueue {public:SeqQueue(int Queuesize 100) {base new ElemType[Queuesize];front 0;rear 0;size Queuesize;};~SeqQueue() {delete[] base;};int Empty_Queue();int En_Queue…

es文件创建局域网服务器,大神来教你XBMC和ES文件浏览器局域网共享

回复可见&#xff0c;谢谢支持~首先电脑要设置&#xff0c;记住电脑的IP地址后面添加局域网用的cd4318d0f703918f9002dcbf523d269758eec45d.jpg (59.67 KB, 下载次数: 7)2014-10-11 10:26 上传ccb48d03918fa0ec39148578259759ee3c6ddb5d.jpg (45.07 KB, 下载次数: 7)2014-10-11…

【实战 Ids4】║ 认证中心之内部加权

本期配套视频&#xff1a;https://www.bilibili.com/video/BV1sJ41197af?p9&#xff08;昔我往矣&#xff0c;杨柳依依&#xff0c;今我来思&#xff0c;雨雪霏霏&#xff09;1、为什么需要在认证内部加权我们知道&#xff0c;认证中心的作用就是用来保护我们的资源服务器&…

C++ class实现链队列(完整代码)

代码如下: #include <iostream> using namespace std; typedef int ElemType;class QueueNode {friend class LinkQueue;public:QueueNode(): next(NULL) {};private:ElemType data;QueueNode *next; };class LinkQueue {public:LinkQueue(): front(NULL), rear(NULL) {…

CDN加速小水管动态应用技巧

不得不说现在大陆和HK的云主机都是小水管模式&#xff0c;由于硬件的快速发展在这种小水管的情况下很难发挥出用户硬件资源的能力&#xff0c;当然可以加水管但费用很高&#xff0c;更多时候会浪费带宽&#xff1b;这个时候我们想到CDN加速&#xff0c;这种资源的好处就是可以把…

C++ class实现二叉树(完整代码,附非递归遍历)

代码如下: #include <iostream> #include <stack> #include <queue> using namespace std; typedef char ElemType; int InOrder_count 0;class BinTreeNode {friend class BinaryTree;public:BinTreeNode(): lchild(NULL), rchild(NULL) {};private:ElemT…

服务器水冷系统仿真,水冷漫谈(三)——水冷散热器仿真方法

水冷散热器的精细化设计是需要借助仿真软件的。目前可以完成水冷仿真的商业软件有很多种&#xff0c;各有优势&#xff0c;在此不做对比。Jason用的是FloTHERM&#xff0c;但是主要的分析方法是一致的。不同于风冷的系统&#xff0c;水冷整个系统比较庞大和复杂。工程中一般不会…

在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

在这篇文章中&#xff0c;我将介绍如何使用ASP.NET Core托管服务运行Quartz.NET作业。这样的好处是我们可以在应用程序启动和停止时很方便的来控制我们的Job的运行状态。接下来我将演示如何创建一个简单的 IJob&#xff0c;一个自定义的 IJobFactory和一个在应用程序运行时就开…

分享一些支持多租户的开源框架

如果你在开发sass应用程序&#xff0c;可以参考或者直接使用这些开源的框架。ASP.NET Boilerplate — Web应用程序框架ASP.NET Boilerplate是专门为新的现代Web应用程序设计的通用应用程序框架。它使用已经熟悉的工具并围绕它们实施最佳实践&#xff0c;以为您提供SOLID开发经验…

服务器操作系统用什么好,服务器操作系统一般用什么

服务器操作系统一般用什么 内容精选换一换客户的SAP系统部署在第三方云&#xff0c;服务器操作系统在华为云支持的列表内&#xff0c;数据库为任意数据库&#xff0c;同时还有如对象存储等的其他云服务。迁移到华为云后&#xff0c;数据库以及操作系统保持不变&#xff0c;系统…