C 弄懂泛型和協變 逆變

2022-03-08 15:55:19 字數 2852 閱讀 7702

《c#權威指南》上在委託篇中這樣定義:

在泛型篇中這樣定義:

一直沒弄懂,或者一時弄懂了也沒記住,一定不怪我!看到乙個博主這樣解釋,秒懂:

《clr via c#》 第三版這樣定義:

總結:協變逆變中的協逆是相對於繼承關係的繼承鏈方向而言的

《clr via c#》 底部有註解說:

協變性指定返回型別的相容性,而逆變性指定引數的相容性。

物件導向中有乙個規則是:子類向上可以轉為基類,但是基類不能向下轉為子類

比如子類student可以通過以下方式轉為基類person:

person p=new student();

或者student s=new student();

person p=s;

但是基類轉為子類這樣轉編譯器就會出錯:

person p=new person();

student s=p;

或者object obj=new object();

string str=obj;

既然子類可以轉為父類,父類不能轉為子類,那我這樣做行不行?新定義了乙個委託,委託返回乙個型別

delegate t myfunc();

void main()

; person p = s;

myfuncfunc1 = () => new student() ;

myfuncfunc2 = func1;

funcfunc3 = () => new student() ;

funcfunc4 = func3;

}public class person

}public class student : person

}public class teacher

}

這樣不行,myfuncfunc2 = func1;編譯器不認編譯失敗,這句**卻funcfunc4 = func3;正常。

我們知道student轉person是合法的轉換,但是編譯器不知道,需要告訴編譯器這段轉換時合法的,沒有必要做強制的型別安全轉換。

怎麼做?宣告型別時指定out關鍵字,如下:

delegate t myfunc();
可以看到編譯器通過。out關鍵字會告訴編譯器student到person是有效轉換

其實func委託的定義也是如此:

public delegate tresult func();//無輸入引數,有返回值。
但要是加如下**呢?

myfuncfunc5 = func1;
編譯還是會不給過的!因為轉換不合法。

那要是 out關鍵字加在委託的入參型別呢

delegate void myfunc(t t);
會告訴你無效

那齣參可以協變,入參是否可以協變呢?

完全不可以,就像父類轉為子類一樣是不合法的

以下**可以正常編譯為什呢?明明object型別不可以轉為string型別

actionaction1 = t => ;

actionaction2 = action1;

而反過來卻是錯的

actionaction3 = t => ;

actionaction4 = action3;

看看action是如何定義的?

public delegate void action(t obj);//泛型委託,無返回值
in關鍵會告訴編譯器,要麼傳遞t作為委託的引數型別,要麼傳遞t的派生型別。string是oject的派生類,所有是正常的.

所以這裡不能單純理解為object轉為string型別了,而要理解為string可以安全的替換掉object,因為string是object的子類呀,

object有的,string都有,這個轉換肯定是安全的。

泛型中的協變和逆變原理與泛型委託一樣

out: 輸出(作為結果),in:輸入(作為引數)

所以如果有乙個泛型引數標記為out,則代表它是用來輸出的,只能作為結果返回,而如果有乙個泛型引數標記為in,則代表它是用來輸入的,也就是它只能作為引數。

為什麼in只能作為輸入引數的逆變?

void main()

public void method(student stu)

public class person

}public class student : person

}public class goodstudent : student

方法體形參可以把子類當成父類來用(傳遞student還是goodstudent物件都無所謂),但是不能把父類當成子類來用(傳遞person物件就出錯了)。【黎克特制替換原則】

為什麼out只能作為返回值的協變?

通常定義乙個變數來接受返回值,父類可以接收子類的資料(這裡可以理解為object obj=str),子類能接收父類的資料嗎(這裡理解為string str = (string)objcet)?肯定不是不能。所以只能是協變

c#4.0之前ienumerable、 icomparable、 iqueryable等介面都不支援可變性,在4.0及之後才支援。因為4.0之前定義的泛型介面沒有新增out、in關鍵字,有興趣可以切換版本看看。

C 泛型的協變和逆變

可變性是以一種型別安全的方式,將乙個物件當做另乙個物件來使用。如果不能將乙個型別替換為另乙個型別,那麼這個型別就稱之為 不變數。協變和逆變是兩個相互對立的概念 在c 4.0之前,所有的泛型型別都是不變數 即不支援將乙個泛型型別替換為另乙個泛型型別,即使它們之間擁有繼承關係,簡而言之,在c 4.0之前...

泛型協變和逆變3

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

關於c 中逆變和協變的理解

逆變和協變都是針對模板類 介面中的引數型別來說的。假定乙個父類father,乙個子類child,乙個模板類sampletemplate 簡單來說 物件導向程式設計中,很容易理解,需要父類的地方,都可以使用子類。反之需要子類的地方,卻不能使用父類。所以協變看起來似乎理所當然,而逆變看起來有點反邏輯。這...