逆变和协变都是针对模板类/接口中的参数类型来说的。
假定一个父类Father, 一个子类Child, 一个模板类SampleTemplate<T>
简单来说
SampleTemplate<Father>的地方,可以传入SampleTemplate<Child>。常见的协变类型:IEnumberableSampleTemplate<Child>的地方,可以传入SampleTemplate<Father>。常见的逆变类型:List<Child>.Sort函数使用的IComparer面向对象编程中,很容易理解,需要父类的地方,都可以使用子类。
反之需要子类的地方,却不能使用父类。
所以协变看起来似乎理所当然,而逆变看起来有点反逻辑。
这里首先看一下,如何定义一个模板类是否支持逆变或者协变。
SampleTemplate<out T>SampleTemplate<in T>这里的in和out是什么意思?
结合前面的IEnumerable<out T>和IComparer<in T>:
IEnumberable中,如果一个函数需要IEnumberable<Father>作为入参,我们传IEnumerbale<Child>也可以,因为我们使用这个IEnumerable时,是从其中获取元素,作为Father处理,所以IEnumerable中的数据是出向的,对应out。所以传入Child类,输出Father类没有问题。List<Child>类的Sort调用IComparer时,Sort函数签名要求的IComparer<Child>,实际表明它会向IComparer传入一个Child类型,而这个类型会在IComparer内部使用,于是对应in入方向。自然只要实际实现的模板类,能接受Child类型即可,实际上也是相当于子类到父类的转换。所以逆变和协变,对应的都是子类向父类的转换(子类也不可能向父类转换),不同点在于协变是模板类可以接收子类参数而输出父类;而逆变是模板类本身是作为被输入的实体,由外部输入子类,模板类把他当作父类使用。
原文:https://www.cnblogs.com/mosakashaka/p/12651662.html