1. 程式人生 > >C# - 協變、逆變 看完這篇就懂了

C# - 協變、逆變 看完這篇就懂了

1. 基本概念

官方:協變和逆變都是術語,前者指能夠使用比原始指定的派生型別的派生程度更大(更具體的)的型別,後者指能夠使用比原始指定的派生型別的派生程度更小(不太具體的)的型別。[MSDN]

公式:

          協變:IFoo<父類> = IFoo<子類>;

          逆變:IBar<子類> =  IBar<父類>;

暫時不理解沒關係,您接著往下看。

2. 協變(Covariance)

1) out關鍵字

     對於泛型型別引數,out

 關鍵字可指定型別引數是協變的。 可以在泛型介面和委託中使用 out 關鍵字。[MSDN] 

2) 魯迅:一張圖勝過千言萬語(圖小看不清,單機滑鼠右鍵 -> 在新標籤頁中開啟圖片)

    

           備註:泛型委託的斜變原理也是一樣的。

        3) 什麼是協變?

           斜變就是對具體成員的輸出引數進行一次型別轉換,且型別轉換的準則是 “里氏替換原則”。

3. 逆變(Contravariance)

 1) in關鍵字

     對於泛型型別引數,in 關鍵字可指定型別引數是逆變的。 可以在泛型介面和委託中使用 in 關鍵字。[MSDN]

 2) 魯迅:一張圖勝過千言萬語(圖小看不清,單機滑鼠右鍵 -> 在新標籤頁中開啟圖片)

      

 

        3) 什麼是逆變?

            逆變就是對具體成員的輸入引數進行一次型別轉換,且型別轉換的準則是 “里氏替換原則”。

4. 自問自答

1)協變、逆變 為什麼只能針對泛型介面或者委託?而不能針對泛型類?

      因為它們都只能定義方法成員(介面不能定義欄位),而方法成員在建立物件的時候是不涉及到物件記憶體分配的,所以它們是型別(記憶體)安全的。

      為什麼不針對泛型?因為泛型類是模板類,而類成員是包含欄位的,不同型別的欄位是影響物件記憶體分配的,沒有派生關係的型別它們是不相容的,也是記憶體不安全的。

2)協變、逆變 為什麼是型別安全的?

      本質上是里氏替換原則,由里氏替換原則可知:派生程度小的是派生程度大的子集,所以子類替換父類的位置整個程式功能都不會發生改變。

3)官方對 協變、逆變 的定義現在是否能看懂?

      上面看懂了,官方定義肯定也是沒問題的。派生程度小可以理解為基類,派生程度大可以理解為子類或派生類,至於為什麼用程度這個詞,是因為繼承鏈的深度是沒限制的。