C# 协变与逆变

用最简单的最有内涵的方式进行概括

在OO哲学里面 有个依赖倒置,这是个oo的核心

就是用父类对象可以代表后面也许动态增加的子类对象从而增加了软件的,可扩展性,和相对稳定性,并且开启了一种oo范式

class A{}

class A1:A{}

class A2:A{}

。。。

用A a 这个对象可以表达A1,A2...等子类的实例  以便于应对一个维度上的需求变更

在C#语言发展中当引入泛型以后,出现了新需求,就是 如何让这种oo范式自然的涵盖到泛型这个新特性,因为泛型,基本上与其参数之间存在某种可预见之

关联。如果不引入协变与逆变的概念那么,很多泛型就不能使用oo的上述这样的常规编程范式。

举个例子说明

如果有个T= 狗类 (class Dog)

那么 有个泛型 Ixx<T> 这里是 Ixx<Dog> 

如果是逆变 则 Ixx<in T> 这告诉接口 Ixx<in T> 所有与T相关的 接口成员都只是输入向而非输出向的,

这样可以使用 Ixx<SubT>  替换 Ixx<in T>了,注意这个替换就有点 oo依赖倒置的意思因为如上所说,它

把这种逻辑延续到了相关的泛型接口和泛型委托上,如果一个接口有in限制或约束那么就可以有这种替换,为什么,有了in 这个限制Dog就可以用SubDog

来替换了呢? ,现实故事是这样的, Ixx<in Dog> 里面全是和狗相关的功能,这时你加入的是哈巴狗,小土狗,或藏獒,这些全是狗这个类别的,用来替换 Ixx<in Dog>

是不会产生歧义的,如果你用 Ixx<Mammalian> 哺乳动物这个类别去代替就会出问题,因为 老鼠,猴子都是哺乳动物,猴子会上树,狗不行,你这样替代就出问题了

如果用父级允许加入到 Ixx<in Dog>里面那么 Dog里面就不单纯了,不止有狗,可能还有鼠或猫 这与定义已经冲突了所以不行。

out呢? 如果 Ixx<out Dog> 这个限制说明这个接口中一切相关Dog参数的成员只有输出而没有输入操作,有了这样的限制前提,那么使用 Ixx<Parent_Dog> 去“替换”

就没问题了,为什么是这样呢?还是回到现实故事中来理解,Ixx<out Dog> 里面有各种狗,哈巴狗,土狗,藏獒什么的,全是狗,他们的最大公约就是其父级狗这个类

,所以使用其父级或给高阶的父级都没问题 象 哺乳动物, 动物,或生物这些 父级都可以,因为这样的替换不会产生歧义,具体的狗的各种功能可能在父级里面不全有,但是父级里面有的

其子类必有,所以这样的替换就无歧义,与之相反你不能使其子级做为输出,比如宠物狗 Ixx<Pet_Dog> 用来替代 Ixx<out Dog>

因为Ixx<out Dog>里面可能包含非 宠物狗,这样的替换逻辑就发生了错误。

简单来说就是视 in 和 out 关键字修饰的不同,可使用不同的泛型参数类,对其进行替换。

综上所述 结合现实故事的梳理我们不难理解 C# 的协变与逆变这两个比较冷门的概念了。

原文地址:https://www.cnblogs.com/ProjectDD/p/11007340.html