关键字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方便大家整体查看,好些报错的验证性代码删了,有兴趣大家可以自己验证一下。

让我们用心感受泛型接口的协变和抗变out和in的更多相关文章

  1. C#中协变与抗变(逆变)

    泛型在.NET 2.0中正式的引入.在使用泛型的过程中,联系上面向对象的继承性.往往很容易想当然敲出类似以下代码 List<Animal> animalLst=new List<Do ...

  2. c# 协变与抗变

    定义 协变:与原始类型转换方向相同的可变性称为协变. 抗变:与派生类型转换方向相同的可变性称为抗变. 补充: 参数是协变的,可以使用派生类对象传入需要基类参数的方法,反之不行 返回值是抗变的,不能使用 ...

  3. C#中泛型方法与泛型接口 C#泛型接口 List<IAll> arssr = new List<IAll>(); interface IPerson<T> c# List<接口>小技巧 泛型接口协变逆变的几个问题

    http://blog.csdn.net/aladdinty/article/details/3486532 using System; using System.Collections.Generi ...

  4. .NET 4.0中的泛型的协变和逆变

    转自:http://www.cnblogs.com/jingzhongliumei/archive/2012/07/02/2573149.html 先做点准备工作,定义两个类:Animal类和其子类D ...

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

    转自:http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html 随Visual Studio 2010 CTP ...

  6. .NET 4.0中的泛型协变和逆变

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

  7. 转载.NET 4.0中的泛型的协变和逆变

    先做点准备工作,定义两个类:Animal类和其子类Dog类,一个泛型接口IMyInterface<T>, 他们的定义如下:   public class Animal { } public ...

  8. 解读经典《C#高级编程》最全泛型协变逆变解读 页127-131.章4

    前言 本篇继续讲解泛型.上一篇讲解了泛型类的定义细节.本篇继续讲解泛型接口. 泛型接口 使用泛型可定义接口,即在接口中定义的方法可以带泛型参数.然后由继承接口的类实现泛型方法.用法和继承泛型类基本没有 ...

  9. C#泛型中的抗变和协变

    在.net4之前,泛型接口是不变的..net4通过协变和抗变为泛型接口和泛型委托添加了一个重要的拓展 1.抗变:如果泛型类型用out关键字标注,泛型接口就是协变的.这也意味着返回类型只能是T. 实例: ...

随机推荐

  1. 【.NET深呼吸】(WPF)跨窗口完成绑定

    在99.999975%的情形下,可视化元素之间的相互绑定都会在同一个容器中完成,如同一个窗口,同一个UserControl等.但在极少数的情况下,为了从最大限度减少代码赋值的前提下考虑,是可以使用跨窗 ...

  2. CSS3与页面布局学习总结(七)——前端预处理技术(Less、Sass、CoffeeScript、TypeScript)

    CSS不像其它高级语言一样支持算术运算.变量.流程控制与面向对象特性,所以CSS样式较多时会引起一些问题,如修改复杂,冗余,某些别的语言很简单的功能实现不了等.而javascript则是一种半面向对象 ...

  3. Vertica 安装,建库,新建测试用户并授予权限,建表,入库

    测试环境:RHEL 6.4 + Vertica 6.1.3-7 需求:搭建Vertica数据库3节点的测试环境,建立测试用户,建表,测试数据入库. 1.各节点关闭防火墙和SELinux,配置主机名,I ...

  4. 一个可随意定位置的带色Toast——开源代码Crouton的简单使用

    今天在公司要求的代码中,要求显示的提示能够更加具有多样化,而不是简单的Toast字样,第一想法肯定是自定义View呀,结果在浏览中发现还有这样的一个开源代码——Crouton. 几经折腾,发现这个东西 ...

  5. jackson简单使用,对象转json,json转对象,json转list

    添加jackson依赖: // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core compile g ...

  6. 剖析 HTTP 协议

    HTTP 概述 HTTP 是什么? HTTP(HyperText Transfer Protocol,超文本传输协议)是WWW (World Wide Web)实现数据通信的基石. HTTP是由IET ...

  7. .NET Core之Entity Framework Core 你如何创建 DbContext

    本文版权归博客园和作者吴双共同所有,欢迎转载,转载和爬虫请注明博客园蜗牛原文地址 http://www.cnblogs.com/tdws/p/5874212.html. 目前国内各大论坛,各位大牛的分 ...

  8. 【无私分享:ASP.NET CORE 项目实战(第四章)】Code First 创建数据库和数据表

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 本章我们来介绍下Asp.net Core 使用 CodeFirst 创建数据库和表,通过 控制台 和 dotnet ef 两种 ...

  9. JsCharts图表的介绍和简单使用

    一.JSCharts介绍 JScharts是一个用于在浏览器直接绘制图表的javascript工 具包.JScharts支持柱状图.圆饼图以及线性图,可以直接将这个图插入网页, JScharts图的数 ...

  10. JAVA 读写Excel

    ExcelUtil.java package pers.kangxu.datautils.utils; import java.io.File; import java.io.FileInputStr ...