协变和逆变

622 阅读2分钟

协变和逆变

约定:

  • A ≦ B 意味着 A 是 B 的子类型
  • A → B 指的是以 A 为参数类型,以 B 为返回值类型的函数类型
  • x : A 意味着 x 的类型为 A

协变和逆变的概念可以借助实际的变量类型来理解:

  • 协变和普通变量:

在 C# 中,List 类实现 IEnumerable 接口,因此 List 实现 IEnumerable:

  IEnumerable<Derived> d = new List<Derived>();
  IEnumerable<Base> b = d;

简单来说,协变就和面向对象概念中的多态一样,指可以使用父类型引用指向子类型实例的情况。

  • 逆变和函数变量:

假如我有这样一个类型链:C ≦ B ≦ A,此时有一个函数是这样的:f(B → B),这个函数接收一个 B → B 函数作为参数, 那么这种情况下,怎样的函数可以作为 f 的参数呢?

首先,对于 C → * 来说都是不行的,因为 f 调用函数时参数类型可能是 B 或 B 的其他子类型,而 C → * 只支持 C 类型的入参。

然后,对于 * → A 来说也是不行的,因为 f 要求的返回值是 B 或 B 的其他子类型,但是 * → A 可能会返回 B 的父类型 A。

最后,对于 A → C 来说却是可行的,因为 f 的参数只会是 B 或 B 的其他子类型,而 A → C 的入参类型是 A,满足。 同时,函数 A → C 的返回值是 C,是 B 的子类型,返回值类型也满足。

这时,神奇的情况便发生了,当函数类型是 B → B 时,我们可以使用 <? super B> → <? extend B> 进行赋值:

  Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
  Action<Derived> d = b;

这就是逆变。

参考: