这里写得并不好,重新整理和修改在我的博客中:https://631320085.github.io/2017/02/01/Convariance_Contravariance.html

关键字out和in相信大家都不陌生,系统定义的很多泛型类型大家F12都或多或少看见了。但是实际中又很少会用到,以前在红皮书里看到,两三页就介绍完了。有的概念感觉直接搬出来的,只是说这样写会怎样,并没有形象的将为什么这么设计,什么时候有用。再加上是翻译的语义很生硬,理解起来很费劲。自然又百度一通,看了一大堆大家各抒己见,这东西还是像一个低分辨率的图片一样,不够清晰。其实现在各种知识点基本都知道大概是怎么回事,怎么用,但是总感觉少点什么,不够高清。于是最近写了个控制台,把各种不够高清或者需要高清显示的知识点捋了一边。果然纸上得来终觉浅,欲知此事写代码。好多东西尝试一遍或者配合使用,的确感觉神清气爽,很多设计知道了缘由,世界都高清了。废话不多说,分享下自认为简单粗暴有效的愚见吧。

------------------------------------------------关于定义的验证-----------------------------------------------------------------------

  既然是泛型out in关键字(非泛型试过了),那就写一个吧。于是

  大意就是只能用于接口和委托的泛型,于是这样写

  果然OK,为什么只能接口和委托,一时半会想不通,先不管,再捋捋。红皮书里写了一堆,不理解没关系,但是说了一个关键点,in修饰的泛型只能是传入参数,out修饰的只能是返回类型。问题又来了,为什么这么设计,想不通,先不管,验证下

  果然,接着捋。改正错误,现在一个泛型接口带in,out关键字的定义好了。接口能干什么,就是实现它啊。实现泛型接口需要指定泛型为具体类型,红皮书里讲了一堆,in,out反正是跟类型转换有关,那这里的in,out肯定是要作用于父类子类才有效果。于是定义一个父类和子类,再实现接口大概就是这样。

  实现时in out的类型可以随意指定,只要满足in是入参,out是返回类型就行了。既然能这样写,那就这样吧。红皮书里写到用了in之后实现接口的类能怎么怎么转换,用out之后能怎么怎么转换,反正一大堆,很绕,重点就是实现后,父类子类分别作为泛型类型之间能倒腾了。管他的,我们自己来倒腾下吧。

-----------------------------------------------------------------关于转换的验证-------------------------------------------------------

  1.现在GT_ClassC 继承自 GT_InterfaceC<GT_Child, GT_Parent>,于是这样写是OK的。

  既然是父类子类对泛型不同实现的转换,那我们就修改左边的父类或子类泛型,看看能不能转换。首先把左边的子类改为父类,于是是两个父类实现的泛型接口。

  结果是不能自动转换,为什么左边的参数从子类变成父类,就报错了。

  来捋一捋,首先左边是in修饰,所以左边的泛型肯定只能是入参类型。我们看看GT_ClassC类Show方法的入参是什么,是子类GT_Child对吧,假设方法体里已经调用了子类的相关成员,这时候我们把入参类型改成他的父类,会有什么问题。子类的成员父类不一定有,这就是关键,方法体有异常风险。所以这里的入参类型的只能兼容GT_Child的同类或子类。

  是不是呼之欲出,in修饰的类型在泛型接口实现后相互转换时,左边的该类型只能是右边该类型的同类或子类。

  再看红皮书里对in抗变的描述,大意父类到子类的转换(这样写谁明白),其实意思应该是:in修饰的类型只能向下兼容,编译器允许转换右边的类型被转换成子类。

  2.那再来看看out吧,out是GT_InterfaceC第二个类型的修饰符,所以我们在刚才不报错的写法上修改下第二个参数,于是

  果然,报错了。那就捋一捋吧。

  out关键字修饰的类型只能是返回类型,看看GT_ClassC中Show方法的返回类型是什么,是GT_Parent对吧。为什么把左边的out类型从父类改为子类就报错了呢。结合上面的理解,因为该类型肯定是返回类型,假设返回的是父类,现在要把父类转换成子类会怎样。看看实验结果

  父类到子类的转换不能隐式转换,因为父类可以有多个子类,转换需要强制转换。所以Show方法的返回类型只能隐式转换成父类。

  是不是呼之欲出:out修饰的类型在泛型接口实现后相互转换时,左边的该类型只能是右边该类型的同类或父类。简单来说out修饰的类型只能向上兼容。和in真的是前呼后应啊。再回头一想,要是in和out不限制:in只能修饰入参类型,out只能修饰返回类型,是不是上面的理论都不能成立,现在知道为什么有这样的约束了吧。

------------------------------------------------------------分析-----------------------------------------------------------------------------------

  下面贴上写代码的时候的一点总结帮助理解

  其实刚开始GT_ClassC实现接口的类型,in out对应的类型是和现在图中颠倒的,这样一来,应该会有人想到了,不管左边是什么类型,右边都能转换成功。所以刚开始我把左边子类父类不管怎么替换都没问题,我以为只要用了in out约束就可以任意写了,但是机智的我立马觉得此事必有蹊跷。要是没问题,还讲什么协变抗变,直接说入参in约束,返回类型out约束就行了。于是有了以上推论。

  看到这是不是觉得该完了。too young, simple is good.

  3.再回头看最开始的疑问,为什么只能是泛型接口或泛型委托可以使用in out。我们写两行代码看看

  定义一个简单泛型,发现不管怎么转换都不行。

  泛型转换是基于接口,先不管具体实现类型是什么,两个同一泛型接口的成员当然可以转换。而具体的类型转换就要基于in和out的约束了(没有关键字约束不管具体实现类型之间有无继承关系都无法转换,已验证)。

  再看看委托,委托可以理解成一种类型,定义(指定参数类型和返回类型的方法)的类型。是不是和泛型很像,事件就相当于委托的实现,是不是有泛型接口的影子。

  泛型委托涉及到泛型,有入参和返回类型当然可以使用in out 约束泛型达到(泛型委托实现互相转换)的编译器检查。

  下面贴上比较完整的demo方便大家整体查看,好些报错的验证性代码删了,有兴趣大家可以自己验证一下。

转载于:https://www.cnblogs.com/xianyudotnet/p/5706991.html

让我们用心感受泛型接口的协变和抗变out和in相关推荐

  1. 【转】c# 协变与抗变

    转自: 协变和抗变 一.定义 在说定义之前,先看一个简单的例子:     public class Shape     {     }       public class Rectange : Sh ...

  2. 用心感受生活,才会发现生活的乐趣。

    持续的努力,才会有持续的收获. 在变好的路上,比计划和行动更宝贵的是在看不到结果时,还能坚持自己的选择. 过好每一天,做好每一件事,用心去感受生活,才会发现生活的乐趣.用心将生活过成自己想要的模样,全 ...

  3. 一张图看完成都云栖大会的精彩,请用心感受!

    什么都不说,请看图!

  4. 漫步者蓝牙自动断开_用心感受音乐带来的愉悦——漫步者W2真无线蓝牙耳机评测...

    对于喜欢听音乐的小伙伴来说,耳机一定是必不可少的物件.常见的耳机一般分为有线耳机和蓝牙无线耳机,有线耳机不用多说,它最大的优点在于使用线材能保证最优的音质,但是在现在快节奏的生活下,小编现在基本都是在 ...

  5. C# Generics 泛型

    C# Generics 泛型 泛型优点 性能 为了让方法传递任何类型的参数,可以用object类来传递参数.值类型转换为引用类型称为装箱,引用类型转换为值类型称为拆箱,需要强制转换.如下代码所示: 数 ...

  6. .NET可变性解析(协变和逆变)

    [一]何为可变性 可变性是.NET4.0中的一个新特性,可变性可分为 : 协变性.逆变性.不可变性. 那么在.NET4.0之前是否有可变性? 答案是肯定的,我们可以通过下面的几个实例来简单的了解一下. ...

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

    一.前言 刘大胖决定向他的师傅灯笼法师请教什么是协变和逆变. 刘大胖:师傅,最近我在学习泛型接口的时候看到了协变和逆变,翻了很多资料,可还是不能完全弄懂. 灯笼法师:阿胖,你不要被这些概念弄混,编译器 ...

  8. .NET 4.0中的泛型协变和反变

    随Visual Studio 2010 CTP亮相的C#4和VB10,虽然在支持语言新特性方面走了相当不一样的两条路:C#着重增加后期绑定和与动态语言相容的若干特性,VB10着重简化语言和提高抽象能力 ...

  9. dns 领克_领克03 1.5T 半年9600公里使用感受分享

    先上一张领克APP 首保的完成说明(不要问为什么配图不是03,颜色也不对,后续还会对这个吐槽 车子详细型号是2019 款 03 1.5T 劲pro 白色,去年10月底提车,落地不算利息 15.6,供各 ...

  10. 仿苹果手机_iPhone苹果手机,谈谈我这些年使用中的体验和感受

    我真正使用苹果手机大约有7年多了,目前用的是iPhone 7 plus ,中间一直没有用过其他品牌的安卓机子.除非家人的手机偶尔使用一下,但是所有的亲戚朋友要我帮他们买手机,我从来没有主动推荐他们买苹 ...

最新文章

  1. centos7下没有iptables进行安装或更新
  2. ajax存到php变量,Ajax返回值作为PHP变量
  3. php header
  4. IOS 学习笔记 2015-03-24 OC-API-常用结构体
  5. 30-10-010-编译-kylin-on-druid-2.6.0-CDH57编译
  6. datatables使用封装
  7. [基于子串搜索的方法] BNDM算法
  8. 2014蓝桥杯:地宫取宝(DFS详解)
  9. OKHttp源码解析(6)----拦截器CallServerInterceptor
  10. ubuntu20.04【一键脚本安装wps并配置字体】
  11. 【Scratch】青少年蓝桥杯_每日一题_12.01_角色装扮
  12. vtk 提取等值面并显示
  13. mysql学习笔记 51_mysql学习笔记
  14. 非线性优化库NLopt简介
  15. 关于vc隐藏浏览器控件 2010-9-7 16:07
  16. Java Web 图说
  17. HDOJ 5498 Tree
  18. 网络实战之单臂路由与三层交换机配置
  19. #ffffff为什么是白色
  20. MosFET/FinFET/GAFET ——鳍式晶体管还能走多远

热门文章

  1. bzoj3453: tyvj 1858 XLkxc(拉格朗日插值)
  2. 《软件需求十步走》阅读笔记6
  3. Redis的安装和使用之二------phpredis与phpRedisAdmin
  4. PHP正则表达式的快速学习方法
  5. 理解Active Directory中用户登录的具体行为
  6. 书------编程书(FoxPro)
  7. 两个前端项目利用iframe进行通信
  8. cheerio获取元素内文本,但不包括其子元素内的文本值的方法
  9. node的module.exports和exports
  10. chown、chgrp 更改文件属主属组