1. 程式人生 > 實用技巧 >協變和逆變

協變和逆變

協變和逆變

約定:

  • 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;

這就是逆變。

參考: