C#提供了一组关键字in&out,在泛型接口泛型委托中,若不使用关键字修饰类型参数T,则该类型参数是不可变的(即不允许协变/逆变转换),若使用in修饰类型参数T,保证“只将T用于输入”,则允许T的逆变转换;若使用out修饰类型参数T,保证“只将T用于输出”,则允许T的协变转换。下面我们解释两个问题:

  • 为什么类型参数T仅用于输出时可以进行协变转换?
  • 为什么类型参数T仅用于输入时可以进行逆变转换?
//定义两个类型
class Animal {}
class Dog : Animal {} //定义两个泛型委托
delegate T OutputOnly<T>(); //只将类型参数T用于输出
delegate void ForInput<T>(T obj); //将类型参数T用于输入 //此时协变的含义是:允许将xxx<Dog>类型的委托转换为xxx<Animal>类型的委托 //对于OutputOnly<T>委托:
OutputOnly<Dog> ood = ReturnADog; //一个假设的符合该委托约束条件的方法,返回一个Dog对象
//对委托类型进行协变转换:
OutputOnly<Animal> ooa = ood;
Animal an = ooa.Invoke();
//调用ooa委托时,它期望获取一个Animal对象,而ooa委托实际保存的ReturnADog方法会返回一个Dog对象,
//对于ooa委托来说,它成功获取到了一个Animal对象,所以在仅输出时协变转换是合法的 //对于ForInput<T>委托:
ForInput<Dog> fid = NeedADog; //一个假设的符合该委托约束条件的方法,要求传入一个Dog对象
//对委托类型进行协变转换:
ForInput<Animal> fia = fid;
fia.Invoke(new Animal());
//调用fia委托时,fia根据要求传入一个Animal对象,而此时fia委托实际保存的方法需要一个Dog对象
//所以传入的Animal对象不符合要求。fia委托其实是懵逼的,因为在它看来你要的就是Animal啊,
//它并不知道实际需要的是Dog对象,所以当将参数T用于输入时,协变转换是非法的 //此时逆变的含义是:允许将xxx<Animal>类型的委托转换为xxx<Dog>类型的委托 //对于ForInput<T>委托:
ForInput<Animal> fia = NeedAAnimal; //一个假设的符合该委托约束条件的方法,要求传入一个Animal
//对委托类型进行逆变转换
ForInput<Dog> fid = fia;
fid.Invoke(new Dog());
//调用fid委托时,fid根据要求传入一个Dog对象,而此时fid委托实际保存的方法需要一个Animal对象
//所以fid委托传入的对象符合要求,所以在用于输入时逆变转换是合法的 //对于OutputOnly<T>委托:
OutputOnly<Animal> ooa = ReturnAAnimal; //一个假设的符合该委托约束条件的方法,返回一个Animal
//对委托类型进行逆变转换
OutputOnly<Dog> ood = ooa;
Dog dog = ood.Invoke();
//调用ood委托时,它期望获取一个Dog对象,而ood委托实际保存的ReturnAAnimal方法会返回一个Animal
//所以ood委托等于是被骗了,它想要的根本不是Animal对象,所以在用于输出时逆变转换是非法的

由此可见,仅用于输出时协变转换才合法,仅用于输入时逆变转换才合法,而in&out关键字正是C#语言设计的用来监督用户“你想用协变/逆变可以,但不许瞎搞类型转换”的关键字,可以说是很优秀的一个特性了。

C#的in/out关键字与协变逆变的更多相关文章

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

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

  2. 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 ...

  3. Programming In Scala笔记-第十九章、类型参数,协变逆变,上界下界

    本章主要讲Scala中的类型参数化.本章主要分成三个部分,第一部分实现一个函数式队列的数据结构,第二部分实现该结构的内部细节,最后一个部分解释其中的关键知识点.接下来的实例中将该函数式队列命名为Que ...

  4. java协变逆变,PECS

    public static void main(String[] args) { // Object <- Fruit <- Apple <- RedApple System.out ...

  5. 协变 & 逆变

    都跟里氏替换原则有关. 协变:你可以用一个子类对象去替换相应的一个父类对象,这是完全符合里氏替换原则的,和协(谐)的变.如:用Swan替换Bird. 逆变:你可以用一个父类对象去替换相应的一个子类对象 ...

  6. C#核心语法讲解-泛型(详细讲解泛型方法、泛型类、泛型接口、泛型约束,了解协变逆变)

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...

  7. C#核心语法-泛型(详细讲解泛型方法、泛型类、泛型接口、泛型约束,了解协变逆变)

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...

  8. 编写高质量代码改善C#程序的157个建议[协变和逆变]

    前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议42.使用泛型参数兼容泛型接口的不可变性 建议43.让接口 ...

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

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

随机推荐

  1. 查询并导出表结构为Excel

    应公司要求将数据库结构用表格形式来展示给客户,最开始我手工弄了两张表效率实在太低了,于是就想偷懒了,就去网上找了一段儿sql查询语句效率提高了70%一执行就出来了,导出查询结果剩下的就只需要调整一下e ...

  2. visual studio进行机器学习与python编写

    visual studio里的python安装之后自带一个虚拟环境 1.anaconda有些包版本无法到最新. 2.包管理器在安装卸载,强制停止后,包管理器会出问题,一直卸不掉那个包. 在卸载pyth ...

  3. Spring Boot 3.0.0 发布第一个里程碑版本M1,你的 Java 升到17 了吗?

    2022年1月20日,Spring官方发布了Spring Boot 3.0.0的第一个里程碑版本M1. 下面一起来来看看Spring Boot 3.0.0 M1版本都有哪些重大变化: Java基线从 ...

  4. yum搭建私有仓库远程版

    目录 一:yum安装 1.简介 1.安装 2.卸载 3.更新 4.yum安装的生命周期 二:yum搭建私有仓库(本地版) 1.下载必须的软件包(准备配置) 2.创建软件仓库 3.下载对应的软件 4.初 ...

  5. 多选菜单shell脚本

    有 很多方法 可以读取Bash脚本中的用户输入. 但是,允许用户键入输入会带来一些问题. 主要问题是输入验证,您必须在其中考虑所有可能的输入. 解决此问题的一种简单方法是为Bash脚本创建一个多选菜单 ...

  6. MySQL单表查询(分组-筛选-过滤-去重-排序)

    目录 一:单表查询 1.单表查询(前期准备) 2.插入记录(写入数据) 3.查询关键字 二:查询关键字之where 1.查询id大于等于3小于等于6的数据 2.查询薪资是20000或者18000或者1 ...

  7. chapter2 线性回归实现

    1 导入包 import numpy as np 2 初始化模型参数 ### 初始化模型参数 def initialize_params(dims): w = np.zeros((dims, 1)) ...

  8. Lesson8——Pandas reindex重置索引

    pandas目录 1 简介 重置索引(reindex)可以更改原 DataFrame 的行标签或列标签,并使更改后的行.列标签与 DataFrame 中的数据逐一匹配.通过重置索引操作,您可以完成对现 ...

  9. Java流程控制03:顺序结构

    顺序结构 Java的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行. 顺序结构是最简单的算法结构.从上到下 语句与语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个依次执行的处 ...

  10. Spring Boot配置多个DataSource (转)

    使用Spring Boot时,默认情况下,配置DataSource非常容易.Spring Boot会自动为我们配置好一个DataSource. 如果在application.yml中指定了spring ...