理解 C# 泛型接口中的協(xié)變與逆變(抗變)

最近在看《C#高級(jí)編程(第九版)》這本書隙笆,看到了泛型接口這章。其中關(guān)于協(xié)變和逆變沒太理解,講得有點(diǎn)坑爹撑柔,網(wǎng)上查了許多資料煤率,總算(感覺)弄清楚了,來這里記錄一下乏冀。

一蝶糯、協(xié)變和逆變是什么?

先從字面上理解 協(xié)變(Covariance)辆沦、逆變(Contravariance)昼捍。
co- 是英文中表示“協(xié)同”、“合作”的前綴肢扯。協(xié)變 的字面意思就是 “與變化的方向相同”妒茬。
contra- 是英文中表示“相反”的前綴,逆變 的字面意思就是是 “與變化方向相反”蔚晨。
那么問題來了乍钻,這里的 變化方向 指的是什么?
C# 中對(duì)于對(duì)象(即對(duì)象引用)铭腕,僅存在一種隱式類型轉(zhuǎn)換银择,即 子類型的對(duì)象引用到父類型的對(duì)象引用的轉(zhuǎn)換。這里的變化指的就是這種 子->父 的類型轉(zhuǎn)換累舷。
object o = "hello";
//string (子類)類型的引用轉(zhuǎn)換為 object (父類)類型的引用
協(xié)變與逆變雖然從名字上看是兩個(gè)完全相反的轉(zhuǎn)換浩考,但其實(shí)只是“子類型引用到父類型引用”這一過程在函數(shù)中使用的 兩個(gè)不同階段 而已,接下來將詳細(xì)說明這點(diǎn)被盈。

二析孽、使用函數(shù)的不同階段發(fā)生的類型轉(zhuǎn)換

假設(shè)有一函數(shù),接收 object 類型的參數(shù)只怎,輸出 string 類型的返回值:

string Method(object o)
{
    return "abc";
}

那么在Main函數(shù)中我們可以這樣調(diào)用它:

string s = "abc";
object o = Method(s);

注意袜瞬,這里發(fā)生了兩次隱式類型轉(zhuǎn)換:

  1. 在向函數(shù)輸入時(shí),參數(shù) s 由 string 類型轉(zhuǎn)換為 object 類型
  2. 在函數(shù)輸出(返回)時(shí)身堡,返回值 由 string 類型轉(zhuǎn)換為 object 類型
    我們這里可以看作是函數(shù)簽名可發(fā)生變換(不論函數(shù)的內(nèi)容邓尤,不影響結(jié)果):
  3. string Method(object o) 可變換為 string Method(string o)
  4. string Method(string o) 可變換為 object Method(string o)
    也就是說,在函數(shù)輸入時(shí)盾沫,函數(shù)的 輸入類型 可由 object 變換為 string裁赠,父->子
    在函數(shù)輸出時(shí),函數(shù)的 輸出類型 可由string變換為object赴精,子->父

三佩捞、理解泛型接口中的 in、out參數(shù)

沒有指定in蕾哟、out的情況
假設(shè)有一泛型接口一忱,并且有一個(gè)類實(shí)現(xiàn)了此接口:

interface IDemo<T>
{
    T Method(T value);
}
public class Demo : IDemo<string>
{
    //實(shí)現(xiàn)接口 IDemo<string>
    public string Method(string value)
    {
        return value;
    }
}

在Main函數(shù)中這樣寫:
IDemo<string> demoStr = new Demo();
IDemo<object> demoObj = demoStr;
上面的這段代碼中的第二行包含了一個(gè)假設(shè):
IDemo<string> 類型能夠隱式轉(zhuǎn)換為 IDemo<object> 類型
這乍看上去就像“子類型引用轉(zhuǎn)換為父類型引用” 一樣莲蜘,然而很遺憾,他們并不相同帘营。假如可以進(jìn)行隱式類型轉(zhuǎn)換票渠,那就意味著:
string Method(string value) 能轉(zhuǎn)換為 object Method(object value)
從上一節(jié)中我們知道,在函數(shù)這輸入和輸出階段芬迄,其類型可變化方向是不同的问顷。所以在C#中,要想應(yīng)用泛型接口類型的隱式轉(zhuǎn)換禀梳,需要討論“輸入”和“輸出”兩種情況杜窄。
接口僅用于輸出的情況,協(xié)變

interface IDemo<out T>
{
    //僅將類型 T 用于輸出
    T Method(object value);
}
public class Demo : IDemo<string>
{
    //實(shí)現(xiàn)接口
    public string Method (object value)
    {
        //別忘了類型轉(zhuǎn)換算途!
        return value.ToString();
    }
} 

在Main函數(shù)中這樣寫:
IDemo<string> demoStr = new Demo();
IDemo<object> demoObj = demoStr;
可將 string Method (object value) 轉(zhuǎn)換為 object Method (object value)
即可將 IDemo<string> 類型轉(zhuǎn)換為 IDemo<object> 類型塞耕。
僅從泛型的類型上看,這是 “子->父” 的轉(zhuǎn)換嘴瓤,與第一節(jié)中提到的轉(zhuǎn)換方向相同扫外,稱之為“協(xié)變”。
接口僅用于輸入的情況廓脆,逆變
同理我們可以給 T 加上 in 參數(shù):

interface IDemo<in T>
{
    //僅將類型 T 用于輸入
    string Method(T value);
}
public class Demo : IDemo<object>
{
    //實(shí)現(xiàn)接口
    public string Method (object value)
    {
        return value.ToString();
    }
} 

在Main函數(shù)中這樣寫:
IDemo<object> demoObj = new Demo();
IDemo<string> demoStr = demoObj;
這里可將 string Method (object value) 轉(zhuǎn)換為 string Method (string value)
即可將 IDemo<object> 類型轉(zhuǎn)換為 IDemo<string> 類型筛谚。
僅從泛型的類型上看,這是 “父->子” 的轉(zhuǎn)換狞贱,與第一節(jié)中提到的轉(zhuǎn)換方向相反刻获,稱之為“逆變”,有時(shí)也譯作“抗變”或“反變”瞎嬉。

四、總結(jié)

以上只討論了協(xié)變與逆變?cè)诜椒ㄖ械那闆r厚柳,其實(shí)在屬性中情況也相類似氧枣,不再說明。
可能大家也發(fā)現(xiàn)了别垮,所謂“協(xié)”與“逆”都是只是一種表象便监,其內(nèi)在本質(zhì)為同一過程。
“協(xié)變”與“逆變”中的“協(xié)”與“逆”表示泛型接口在將類型參數(shù)僅用于輸入或輸出的情況下碳想,其類型參數(shù)的隱式轉(zhuǎn)換所遵循的規(guī)律烧董。

協(xié)變

當(dāng)泛型接口類型僅用于輸出(使用關(guān)鍵詞 out),其類型參數(shù)隱式轉(zhuǎn)換所遵循的規(guī)律與對(duì)象引用的類型轉(zhuǎn)換規(guī)律相同胧奔,稱之為“協(xié)變”

逆變

當(dāng)泛型接口類型僅用于輸入(使用關(guān)鍵詞 in)逊移,其類型參數(shù)隱式轉(zhuǎn)換所遵循的規(guī)律與對(duì)象引用的類型轉(zhuǎn)換規(guī)律相反,稱之為“逆變”龙填、“抗變”或“反變”胳泉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拐叉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扇商,更是在濱河造成了極大的恐慌凤瘦,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件案铺,死亡現(xiàn)場(chǎng)離奇詭異蔬芥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)控汉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門笔诵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人暇番,你說我怎么就攤上這事嗤放。” “怎么了壁酬?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵次酌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我舆乔,道長(zhǎng)岳服,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任希俩,我火速辦了婚禮吊宋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颜武。我一直安慰自己璃搜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布鳞上。 她就那樣靜靜地躺著这吻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篙议。 梳的紋絲不亂的頭發(fā)上唾糯,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音鬼贱,去河邊找鬼移怯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛这难,可吹牛的內(nèi)容都是我干的舟误。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼雁佳,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼脐帝!你這毒婦竟也來了同云?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤堵腹,失蹤者是張志新(化名)和其女友劉穎炸站,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疚顷,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旱易,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腿堤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阀坏。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笆檀,靈堂內(nèi)的尸體忽然破棺而出忌堂,到底是詐尸還是另有隱情,我是刑警寧澤酗洒,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布士修,位于F島的核電站,受9級(jí)特大地震影響樱衷,放射性物質(zhì)發(fā)生泄漏棋嘲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一矩桂、第九天 我趴在偏房一處隱蔽的房頂上張望沸移。 院中可真熱鬧,春花似錦侄榴、人聲如沸雹锣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)笆制。三九已至,卻和暖如春涣达,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背证薇。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工度苔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浑度。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓寇窑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親箩张。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甩骏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 泛型(Generics)的型變是Java中比較難以理解和使用的部分饮笛,“神秘”的通配符咨察,讓我看了幾遍《Java...
    珞澤珈群閱讀 7,770評(píng)論 12 51
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法福青,內(nèi)部類的語(yǔ)法摄狱,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法无午,線程的語(yǔ)...
    子非魚_t_閱讀 31,587評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理媒役,服務(wù)發(fā)現(xiàn),斷路器宪迟,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 前言 人生苦多酣衷,快來 Kotlin ,快速學(xué)習(xí)Kotlin次泽! 什么是Kotlin穿仪? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,146評(píng)論 9 118
  • object 變量可指向任何類的實(shí)例,這讓你能夠創(chuàng)建可對(duì)任何數(shù)據(jù)類型進(jìn)程處理的類箕憾。然而牡借,這種方法存在幾個(gè)嚴(yán)重的問題...
    CarlDonitz閱讀 908評(píng)論 0 5