應該怎麼理解程式語言中的協變逆變?

時間 2021-06-01 16:36:46

1樓:花生炒花生

約定:A ≦ B 意味著 A 是 B 的子型別

A → B 指的是以 A 為引數型別,以 B 為返回值型別的函式型別

x : A 意味著 x 的型別為 A

協變和逆變的概念可以借助實際的變數型別來理解:

協變和普通變數:

在 C# 中,List 類實現 IEnumerable 介面,因此 List 實現 IEnumerable:

IEnumerable

>d=newList

>();

IEnumerable

>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 b = (target) => ;

Action d = b;

這就是逆變。

參考:協變與逆變 - 維基百科,自由的百科全書

泛型中的協變和逆變 | Microsoft Docs

協變與逆變 | 深入理解 TypeScript

2樓:Liuyl

在typescript裡,引數是contra的const A = (a: string & number):void

type R = A extends (p: infer P):void ? P : never

R = string & number

而convar推斷會導致

R = string | number

因為在函式引數裡,應該收緊型別而不是放開型別。

3樓:小白龍

支援物件導向正規化的程式語言裡,子類是全真包含父類的,也就是子類繼承了父類,並擴充套件了父類。

因此,對於乙個要求傳遞父類作為引數的場景,開發者實際上傳遞子類的例項是完全合法的,編譯器對此不會有任何錯誤提示,實際執行中也不會產生錯誤。這就是協變。

而對於乙個要求傳遞子類作為引數的場景,開發者實際上傳遞了父類的例項則是不合法的,因為這個場景中很可能使用了子類擴充套件的成員,而這些成員並沒有定義在父類中。檢查和保證安全性的責任就落在了開發者的身上,同時某些語言的編譯器要求開發者使用強制轉型以確保語法上的正確性,否則拒絕編譯。這就是逆變。

4樓:SPACE NEWS

程式語言裡乙個真理就是父類引用可以指向子類例項比如大筐 base b =小筐 new child()這就像乙個父類大筐裡面能放下乙個子類小筐

具體到協變抗變舉個例子

A function (B)

D ret = function(F);

如上面所示根據大筐裝小筐理論

D只能是A或者A的父類,這就是抗變。已經有個小筐了你要裝他只能用大筐不能用可以變小的小筐來裝小筐因此這個大筐不能支援變小就是大筐要抗變(小)

F必須是B或者B的子類,這就是協變。就是給你個大筐在那裡放著你要裝只能裝個可以變小的小筐進去也就是放進去的筐可以變小也就是小筐要協變(小)。

5樓:小蝶驚鴻

具體呢,有兩種,一種是存在於委託中,另外一種存在於集合中更具體呢,請看下面這兩篇資料:

泛型中的協變和逆變

委託中的協變和逆變 (C#)

6樓:啐樓

其實這個涉及到的就是黎克特制替換原則。

對c#不熟,還是來c++的吧。

Covariance就是一種約束,什麼約束呢,與繼承正方向一致的約束。

c++中有個強力的約束,override function的返回值必須是一樣的,否則不認為是override,

但有個特例,返回值可以Covariance,他的意思很簡單,其實就是黎克特制替換原則。

//繼承關係B:

ARB:RA

;//convarRA*

A::();RB*B

::f();autor=

pA->

f();

Contra就是反著來,是繼承反方向一致的約束

假如,你的返回值Contra就不行了,你反著來,子類返回值更緊縮,這肯定是不行的。

如果你說

//contraRB*

A::();RA*B

::f();//這肯定是不行的。(違反RA繼承鏈下的替換原則)

Contra啥時候用到呢,在引數裡面用

其實道理是相同的,

假設,假設引數型別是T,我們讓引數Convar,那麼子類的引數接受T的子型別,那就不行了,

很簡單,因為違反替換原則啊。

想一想pA

->(ARGA

*arg

);如果子類convar了,那麼子類就允許更大範圍的輸入,那肯定是不行的,

但是你反過來就行了,你contra就完全沒問題。

程式語言中內建型別是怎麼實現的?

Yunfei Lu 都有。一種語言,首先有基本型別,其實就是資料在記憶體的布局,方便表示不同種類的資料,例如整型和浮點是一定要有的,因為cpu的規範。再組合得到陣列,函式型別等,再發展出代數型別,然後包裝成介面 類 泛型等高階概念。基本型別在編譯器裡規定。至於標準庫中定義的型別,通常是某種組合的封裝...

程式語言中的 關鍵字 是怎麼實現的?自己該怎樣實現乙個關鍵字呢?

關鍵字本身是沒有功能的,要關聯功能,有不同的方式 對應到語法樹結構上 對應到一條或多條IR上 或者也可以直接對應到一條或一組彙編指令上 直接對應的到彙編一步到位的方式開發比較困難,除非你的語言相當簡單 關鍵字除了助記,乙個隱含的意義就是,在當前的語言環境下,其功能不可分割,它的分割在實現層。if 有...

關於小白入程式設計應該怎麼選擇適合的程式語言

古月悅涵 第一門語言入門可能要三個月,以後新學語言可能只要三天,就可以直接上手開始做開發了。因此不要猶豫,立即開始學習,不要糾結中拖延等待。多學一門語言也挺好,半年後就知道糾結哪門語言毫無意義。白菜好吃還是蘿蔔好吃?都吃吃試試不就知道了?此外,學什麼語言,明確你學習的目的,將來具體要做什麼,就很容易...