C 中協變與逆變的個人理解

2021-12-30 00:29:08 字數 1465 閱讀 3777

讀了園子中一些前輩的關於c#中協變與逆變的文章,收穫很大,分享一下我的個人理解,希望用較淺顯的方式理解這個比較繞彎的概念。

協變與逆變應該是clr的特性,我僅對我熟悉的c#舉例說明。

說白了,它主要解決的是乙個型別轉換的問題,用乙個最簡單的泛型表示式就是:

s = s

當然這只是乙個抽象的表示式,而且只包含了乙個泛型型別引數,意思是將乙個s的例項賦值給乙個s的例項。s可能是乙個介面或委託,t1和t2是有父子關係(或子父關係)的兩個引用型別。在.net4.0之前,這樣的直接轉換是不可能的,有時我們不得不寫一些轉換函式來實現。協變與逆變可以在一定程度上方便地解決這個問題。

我們來看看怎麼理解這個表示式。

s是暴露給使用者的型別,s是真正做事的型別,所以通俗來講,作為使用者,我們交給s的引數實際是交給了s,而我們通過s拿到的返回值實際是s給我們的。

畫個圖來表示就是:

圖雖然粗糙,說明道理即可。可以看出,如果要使表示式s=s成立,必須滿足兩點:

s中的所有引數都必須型別安全地轉換為s中的對應引數;

s中的所有返回值都必須型別安全地轉換為s的對應返回值。

當然如果引數或者返回值的型別是與泛型型別無關的型別,那麼上面兩條必然滿足。而且需要尤其注意的是:這裡的引數和返回值型別未必是泛型引數型別本身,可能就是t1,也可能是a,還可能是a>>等等,但無疑都要滿足上面的兩個條件。

我認為理解了上面的關係之後,協變與逆變自然可以一步步推理出來了。

假設tbase是tchild的父類,如果滿足s=s就說s支援對t的協變,.net4.0有個新寫法s。

那麼應用上面的兩個規則看看會發生什麼:

s中的所有引數都必須型別安全地轉換為s中的對應引數;

如果引數型別就是t本身,那麼要求tbase可以型別安全地轉換為tchild,即tchild=tbase,父類怎麼可能型別安全地轉換為子類?所以泛型型別引數絕對不可能出現在引數列表中,否則會引發編譯的錯誤。

如果引數型別是a,那麼要求a可以型別安全地轉換為a,即a=a。這要求a要支援對t的逆變,即a。所以我們自然而然推出了乙個重要原則:方法引數的協變-逆變互換原則(該原則取自裝配腦袋的博文:

如果引數是更複雜的多層巢狀,那麼遞迴套用該原則即可。

s中的所有返回值都必須型別安全地轉換為s中的對應返回值;

如果引數型別是t型別本身,那麼要求tchild可以型別安全地轉換為tbase,即tbase=tchild,這個毫無疑問沒問題。所以t型別可以出現在返回值的位置上。

如果引數型別是a,那麼要求a可以型別安全地轉換為a,即a=a。這要求a支援對t的協變,即a。又乙個重要原則:方法返回值的協變-逆變一致原則。

同上。

逆變可以採用同樣的推理過程,不再詳述。

協變與逆變都是我們在定義這個介面或者委託的時候規定好的,至於引數與返回值的限定都只是這個規定產生的結果。一步步縷下來思路還是比較清晰的,雖然不像tbase=tchild這樣一眼可以看出,但起碼有個推理的方向,實在不行只要想想這個醜陋的就好了。

C 協變與逆變

用最簡單的最有內涵的方式進行概括 在oo哲學裡面 有個依賴倒置,這是個oo的核心 就是用父類物件可以代表後面也許動態增加的子類物件從而增加了軟體的,可擴充套件性,和相對穩定性,並且開啟了一種oo正規化 class a class a1 a class a2 a 用a a 這個物件可以表達a1,a2....

c 協變和抗變 C 中協變與抗變(逆變)

泛型在.net 2.0中正式的引入。在使用泛型的過程中,聯絡上物件導向的繼承性。往往很容易想當然敲出類似以下 list animallst new list 顯然這樣編譯是不通過的。雖然dog和animal之間有繼承性,但是list和list這兩個類之間並沒有繼承性。如果要解決這樣的問題,用上協變與...

協變與逆變

目錄 1.協變 2.逆變 在泛型之前,我們都知道可以將乙個派生類物件賦值給基類變數,這叫做賦值相容性。看下面這個例子 相容性示意圖如上。但是當我們新增泛型機制時 class animal class dog animal delegate t factory class program static...