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. vscode搜索高亮个性化设置

    "workbench.colorCustomizations": { "editor.selectionHighlightBorder": "#1ED ...

  2. day23 结构体

    (1).若有说明和定义: typedef int *integer: integer p,*q: 则下列叙述正确的是[C] (A).q是基类型位int的指针变量 (B).p是int型变量 (C).p是 ...

  3. day 16 int *p; scanf("%d",p);问题

    (1).以下不能用于描述算法的是[A] (A).E-R图 (B).伪代码 (C).计算机语言 (D).流程图 分析:E-R图也称实体-联系图,提供了表示实体类型.属性和联系的方法,用来描述现实世界的概 ...

  4. Electron+Vue开发跨平台桌面应用

    Electron+Vue开发跨平台桌面应用 xiangzhihong发布于 2019-12-23 虽然B/S是目前开发的主流,但是C/S仍然有很大的市场需求.受限于浏览器的沙盒限制,网页应用无法满足某 ...

  5. Spring Security源码解析一:UsernamePasswordAuthenticationFilter之登录流程

    一.前言 spring security安全框架作为spring系列组件中的一个,被广泛的运用在各项目中,那么spring security在程序中的工作流程是个什么样的呢,它是如何进行一系列的鉴权和 ...

  6. 【C++】指针和函数

    指针和函数 标签:c++ 目录 指针和函数 一.基本概念 定义: 指针的定义及使用: 二.指针的相互赋值 三.指针的运算 比较大小: 相减: 加减整数类型变量或者常量: 自增自减: 下标运算符[ ]: ...

  7. C++多线程之互斥锁和超时锁

    #include<iostream> #include<thread> #include<mutex> using namespace std; mutex mu; ...

  8. for each ……in

    使用一个变量迭代一个对象的所有属性值,对于每一个属性值,有一个指定的语句块被执行. 作为ECMA-357(E4X)标准的一部分,for each...in语句已被废弃,E4X中的大部分特性已被删除,但 ...

  9. Go 面向对象编程应用

    #### Go 面向对象编程应用前面学习了很多的基础知识,这一节来实际写一个小案例:涉及到的知识: 1. 数组的基本使用2. 结构体3. 切片 4. 方法5. 循环6. 函数返回值(命名返回值,普通返 ...

  10. Nginx命令(全局配置文件与模块)

    目录 一:Nginx命令 二:Nginx全局配置文件 1.nginx全局配置 2.过滤出Nginx 三:Nginx网址模块(解析) 一:Nginx命令 1.-v : 打印版本号 [root@web01 ...