关于c#中逆变和协变的理解

逆变和协变的解释

逆变和协变都是针对模板类/接口中的参数类型来说的。
假定一个父类Father, 一个子类Child, 一个模板类SampleTemplate<T>

简单来说

  • 协变(covariant): 需要SampleTemplate<Father>的地方,可以传入SampleTemplate<Child>。常见的协变类型:IEnumberable
  • 逆变:需要SampleTemplate<Child>的地方,可以传入SampleTemplate<Father>。常见的逆变类型:List<Child>.Sort函数使用的IComparer

理解

面向对象编程中,很容易理解,需要父类的地方,都可以使用子类。
反之需要子类的地方,却不能使用父类。

所以协变看起来似乎理所当然,而逆变看起来有点反逻辑。

这里首先看一下,如何定义一个模板类是否支持逆变或者协变。

  • 协变: SampleTemplate<out T>
  • 逆变: SampleTemplate<in T>

这里的inout是什么意思?

结合前面的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类型即可,实际上也是相当于子类到父类的转换。

所以逆变和协变,对应的都是子类向父类的转换(子类也不可能向父类转换),不同点在于协变是模板类可以接收子类参数而输出父类;而逆变是模板类本身是作为被输入的实体,由外部输入子类,模板类把他当作父类使用。

猜你喜欢

转载自www.cnblogs.com/mosakashaka/p/12651662.html