C 泛型的協變和逆變

2021-09-06 22:49:45 字數 2606 閱讀 2255

可變性是以一種型別安全的方式,將乙個物件當做另乙個物件來使用。如果不能將乙個型別替換為另乙個型別,那麼這個型別就稱之為:不變數。協變和逆變是兩個相互對立的概念:

在c# 4.0之前,所有的泛型型別都是不變數——即不支援將乙個泛型型別替換為另乙個泛型型別,即使它們之間擁有繼承關係,簡而言之,在c# 4.0之前的泛型都是不支援協變和逆變的。

c# 4.0通過兩個關鍵字:outin來分別支援以協變和逆變的方式使用泛型。

我們來看一段利用了協變型別引數的**:

public class baseclass

public class derivedclass : baseclass

下面我們利用協變型別引數,可以執行類似於普通的多型性的分配:

ienumerabled = new list();

ienumerableb = d;

在上面的例項中,在c# 4.0之前是不能正常編譯的,除了對賦值給基類集合時將子類集合做乙個強制轉換,但是在執行時仍然會丟擲乙個型別轉換的異常。

下面我們再看乙個關於逆變的例項**:

actionb = (target) => ;

actiond = b;

d(new derivedclass());

在上面的示例中我們action型別的委託分配給型別action的變數,根據逆變的定義我們可以知道action型別是支援逆變的。

為什麼ienumerableaction可以分別支援型別的協變和逆變呢?我們檢視這兩個型別在 .net 中的定義:

//ienumerable介面的定義(支援協變)

public inte***ce ienumerable: ienumerable

//action委託的定義(支援逆變)

public delegate void action(t obj);

為了保證型別的安全,c#編譯器對使用了outin關鍵字的泛型引數新增了一些限制:

1. 不支援類的型別引數的可變性

只有介面和委託可以擁有可變的型別引數。inout修飾符只能用來修飾泛型介面和泛型委託。

2. 可變性只支援引用轉換

可變性只能用於引用型別,禁止任何值型別和使用者定義的轉換,如下面的轉換是無效的:

3. 型別引數使用了 out 或者 ref 將禁止可變性

對於泛型型別引數來說,如果要將該型別的實參傳給使用out或者ref關鍵字的方法,便不允許可變性,如:

delegate void somedelegate(ref t t)
這段**編譯器會報錯。

4. 可變性必須顯式指定

從實現上來說編譯器完全可以自己判斷哪些泛型引數能夠逆變和協變,但實際卻沒有這麼做,這是因為c#的開發團隊認為:

必須由開發者明確的指定可變性,因為這會促使開發者考慮他們的行為將會帶來什麼後果,從而思考他們的設計是否合理。

5. 注意破壞性修改

在修改已有**介面的可變性時,會有破壞當前**的風險。例如,如果你依賴於不允許可變性的is或as操作符的結果,執行在.net 4時,**的行為將有所不同。同樣,在某些情況下,因為有了更多可用的選項,過載決策也會選擇不同的方法。所以在對已有**引入可變性時要做好足夠的單元測試以及防禦措施。

6. 多播委託與可變性不能混用

下面的**能夠通過編譯,但是在執行時會丟擲argumentexception異常:

funcstringfunc = () => "";

funcobjectfunc = () => new object();

funccombined = objectfunc + stringfunc;

funcstringfunc = () => "";

funcdefensivecopy = new func(stringfunc);

funcobjectfunc = () => new object();

funccombined = objectfunc + defensivecopy;

協變和逆變

泛型中的協變和逆變

委託中的協變和逆變

《深入理解c#》:13.3 介面和委託的泛型可變性

《effective c#》:條目29:支援泛型協變和逆變

《clr via c#》:12.5 委託和介面的逆變和協變泛型型別實參

泛型協變和逆變3

原文 01.協變性指的是 泛型型別引數可以從乙個派生類隱式轉化為基類 子類可以隱式的轉換為父類 例如string 可以轉化為object 在c 4.0中引入out關鍵字來標記泛型引數支援協變性 list.addrange ienumerable 方法 將其元素新增到 list的末尾的集合。集合自身不...

C 泛型 協變和抗變

例如,可以給乙個需要 shape 引數的方法傳送 rectangle 引數嗎?下面用示例說明這些擴充套件的優點。在.net中,引數型別是協變的。假定有 shape 和 rectangle 類,rectangle派生自 shape基類。宣告 display 方法是為了接受 shape 型別的物件作為其...

C 協變逆變

1.泛型的協變逆變 注意 只能放在介面或者委託的泛型引數前面 1 委託泛型引數 static class testconsole private static argumentexception test1 object obj private static exception test2 stri...