首页 > 编程语言 >【C#开发】C#的协变和逆变

【C#开发】C#的协变和逆变

时间:2024-07-19 23:18:18浏览次数:6  
标签:C# Dog 逆变 Animal 协变 泛型 方法 Out

当时第一次学到协变和逆变的时候印象不是很深,不是很能理解。
结果很快想起在刷leetcode题的时候遇到关于这个问题的情况。




问题的出现

就比如力扣第15题三数之和

image

他的返回值是IList<IList< int >> 代表意思类似二维数组。

我第一反应就是先声明一个List<List< int >> res 的变量来作为答案,
并理所应当的认为泛型参数是支持隐式转换的。

但并不是这样滴
image

你得写成 List<IList< int >> 才行
image





这时我不禁新生疑问,难度他真的不支持隐式转换吗?
于是开始上网查找资料,自己研究总结。



假设可以

我们首先思考如果接口泛型参数真的支持隐式转换会发生什么
首先Dog是Animal类的子类
假设35行不会报错,能正常执行
image
因为抽象接口泛型传入的是Animal类,
所以我们会发现36行的In方法是可以传Animal类的,但是我们的实现类的泛型是Dog类,
这就意味着具体实现类的In方法是把这个泛型T看做Dog来处理的
分析一下,这是因为抽象接口的In方法仅要求传入一个Animal类即可,
但是我们的具体实现类的In方法却是对Dog类进行处理。



假如具体实现类的In方法会说话,他会说,我要一个Dog类,你却传一个Animal类给我?
这时的In方法相当于要你子类装父类,明显是不合理,是具有类型安全风险的。



但是仔细观察的话,Out方法却可以
image



因为接口的Out方法输出的是父类Animal
尽管实现类Out方法输出的是Dog
但是相当于父类装子类:Animal res = new Dog(); //是合理的


豁然开朗

所以这时候协变的作用就出来了
在泛型参数T前加入out 关键字
image
35行的报错就解决了



使用协变(out)修饰T之后,T只能作为接口方法的返回值
所以把先前写的In方法注释了



如果理解了上面的协变的例子,那么逆变就好理解了。



我们将上面例子的泛型参数调过来,
抽象接口泛型用子类Dog,具体实现类泛型用父类Animal
image
跟上面协变的例子同理
但这次是In方法没问题,Out方法出现了问题。



分析一下,我们发现抽象接口的In方法要求传入一个Dog类,
但是我们的具体实现类的In方法仅仅要求Animal类。
我要的是父类,你给我子类,这很合理,父类装子类。



但是Out方法就有疑问了
image
分析一下,我们发现抽象接口的Out方法会输出一个Dog类,
但是我们的具体实现类的Out方法仅会输出Animal类。
我要输出的是子类,你却给我父类,这很合理吗?




此时根据多态的原理,entity可能会说:
怎么编译之前我Out方法输出的是一个Dog具体类型
但是编译完之后我Out方法输出的就是更抽象的一个Animal抽象类型了?




这并意味着这两个不能一起用,仅仅只是不能两个同时修饰同一个泛型参数T而已
可以一个用out(协变)来修饰T1,另一个用in(逆变)来修饰T2
image

回到开头

所以为什么IList<IList< int >> 不能装入List<List< int >>呢?
这时我们去翻看IList源码能发现,原来他并没有使用协变
image
因为他的泛型T既要作为方法的参数输入和输出
所以才用不了协变,IList<IList< int >> 才不装不了List<List< int >>



但值得注意的是IEnumerable是支持协变
image



所以IEnumerable<IEnumerable< int >> 就能装List<List< int >>了
image

总结

什么是协变和逆变?
协变 允许你在泛型中使用比指定类型更具体的类型。
例如IEntity < Animal > entity = new Entity< Dog >();
它适用于返回值类型,使用 out 关键字标记泛型类型参数。


逆变 允许你在泛型中使用比指定类型更抽象的类型。
例如IEntity < Dog > entity = new Entity< Animal >();
它适用于参数类型,使用 in 关键字标记泛型类型参数。


为什么需要协变和逆变?
在处理复杂的类型层次结构和泛型集合时,协变和逆变确保类型安全并提高代码的灵活性。
它能够允许我们在不牺牲类型安全的情况下实现"属于泛型的多态性"。

标签:C#,Dog,逆变,Animal,协变,泛型,方法,Out
From: https://www.cnblogs.com/asyaB404/p/18312451

相关文章

  • [ARC173E] Rearrange and Adjacent XOR 题解
    题目链接点击打开链接题目解法太牛了!!!这道题我的第一反应是从高位往低位确定,但这样就全错了换个角度考虑,找最后的答案可以由那些数异或而成这里给出结论:答案一定是偶数个数的异或和(在\(n\%4=2\)且\(n\neq2\)时,不能由全集构成)证明:选出的数非全集的情况用数学归纳法......
  • 设置ssh登陆终端的欢迎信息(linux登录配置,/etc/motd有趣的图案【佛祖保佑】)
    设置ssh终端登陆后的欢迎信息是个很实用的技巧,可以给登陆机器的用户发布一些公告信息,或者做一些有趣的字符图案展示。在这里分享我所知道的两种方法:1.系统级别的提示(即系统的所有用户登陆后都能看到)这个很简单,以root用户身份修改/etc/motd这个文件,将想要展示的文字写入此文件,......
  • Keil烧录时出现Error: Flash Download failed - “Cortex-M0+“的解决办法
    在对MSPM0L1306mini板使用dapLink烧录例程时,程序能正常编译,但烧录时出现Error:FlashDownloadfailed - "Cortex-M0+"解决办法(同一个方法两种操作)操作1:操作2:两种操作最后打开的页面相同,最后几步操作也相同:点击【OK】保存修改烧录成功......
  • Electron 应用关闭突出程序坞
    在Electron应用中,处理窗口关闭并使其最小化到Mac系统的程序坞(Dock)而不是完全退出应用,通常涉及到监听窗口的关闭事件(close事件)并在适当时机阻止其默认行为。以下是一些步骤和关键点,帮助实现这一功能:1.监听窗口关闭事件在Electron的主进程(mainprocess)中,你需要为窗口(Browse......
  • Jmeter实现本地文件的读写操作-将响应结果写入到本地Excel中
    一、环境准备1、引入操作EXcel文件的包2、安装JMeter:确保已安装JMeter。Java环境:确保系统中已安装JDK,并配置了JAVA_HOME环境变量。二、脚本准备1.配置JMeter测试计划创建线程组添加线程组:右键点击“测试计划(TestPlan)”,选择“添加(Add)”->“线程(Threads(Use......
  • ## 学习笔记day05-C语言基础:控制语句 if else、do while、for循环
    day05六、控制语句1.顺序语句​在模块内部没有分支、跳转、循环等条件时,程序按照顺序执行2.分支语句选择语句if...else单分支if(表达式){代码块;} step:先判断表达式是否为真(非0为真,0为假)如果为真,进入if选择结构,执......
  • DHCP协议-------动态主机配置协议
    一、DHCP概述作用:用来为终端分配IP地址,并且对IP地址进行集中化管理的协议。应用层协议;传输层使用UDP协议进行数据封装,端口号67/68,其中68代表客户端;67代表服务端报文类型DHCPdiscover:发现报文,用来发现网络中的DHCP服务器,使用的是广播报文。DHCPoffe......
  • 算法第十一天:leetcode707.设计链表
    一、设计链表的题目描述与链接  707.设计链表的链接如下表所示,您可直接复制下面网址进入力扣学习,在观看下面的内容之前一定要先做一遍哦,这样才能印象深刻!https://leetcode.cn/problems/design-linked-list/https://leetcode.cn/problems/design-linked-list/题目描述:你......
  • MongoRepository 操作 AWS DocumentDB时,如何达到与MySql 中有 select … for update
    在MySQL中,SELECT...FORUPDATE用于在事务中对读取的数据行加锁,以防止其他事务同时修改这些行。这种行级锁定机制在关系型数据库中广泛使用,以确保数据一致性。在MongoDB或AWSDocumentDB中,类似的效果可以通过以下方式实现:使用FindandModify操作:MongoDB提供了f......
  • C#/.NET这些实用的编程技巧你都会了吗?
    DotNetExercises介绍DotNetGuide专栏C#/.NET/.NETCore编程常用语法、算法、技巧、中间件、类库练习集,配套详细的文章教程讲解,助你快速掌握C#/.NET/.NETCore各种编程常用语法、算法、技巧、中间件、类库等等。GitHub开源地址:https://github.com/YSGStudyHards/DotNetExercise......