五分鐘看完,徹底理解協(xié)變逆變
前言
其實(shí)這是c#的老知識點(diǎn)了,但是發(fā)現(xiàn)同事對這個(gè)竟然還一知半解,就和他們講解了下,順便也回顧了下,同事我也把我對這個(gè)的全部理解,融化成幾分鐘的講解,保證大家5分鐘內(nèi)全部理解,看不懂來打我。
協(xié)變、逆變 解決的問題
泛型類型轉(zhuǎn)換
比如Person類是Student的父類,我們平時(shí)可以直接:
Person A = new Student();
這是所謂的隱式轉(zhuǎn)換,相信百分之999.99%的人都知道。
然后隨著大家寫代碼越來越多,就會(huì)遇到這樣的場景。
//我有一個(gè)集合
//我手上有一批學(xué)生
IEnumerable<Student> students = new List<Student>();
//我要他們先做人
IEnumerable<Person> peoples = students;
第一次看到這種代碼,其實(shí)哪怕你一點(diǎn)不知道協(xié)變,逆變,你也覺得這是一段正常不過的代碼,因?yàn)槊總€(gè)學(xué)生都是人,都可以直接轉(zhuǎn)成 人這個(gè)類型,那我一批學(xué)生不就是一批人嗎。
是的,你這樣想絕對沒錯(cuò),不然微軟怎么會(huì)能讓你這樣寫沒問題還編譯通過呢?
但是如果我自己寫一個(gè):
//定義一個(gè)工作的泛型接口
public interface IWork<T>
{
}
實(shí)現(xiàn)類
public class Work<T> : IWork<T>
{
}
//直接報(bào)錯(cuò)
IWork<Person> work = new Work<Student>();
現(xiàn)實(shí)給了我們當(dāng)頭一棒,這時(shí)候,我們應(yīng)該找到 IEnumerable,選中然后狠狠的F12去看一下,為什么官方的就可以。
我們發(fā)現(xiàn)官方在泛型前面多了一個(gè)out關(guān)鍵字。破案了~
現(xiàn)在我們在我們的代碼中也加入out關(guān)鍵字
public interface IWork<out T>
{
}
public class Work<T> : IWork<T>
{
}
IWork<Person> work = new Work<Student>();
OK~代碼正常運(yùn)行。
原則核心
這里開始我們挑戰(zhàn)五分鐘速通,如果按照正常博客上來先講概念,別說五分鐘了,可能大家也就迷迷糊糊地看完了,所以我們直接整活。
核心依據(jù)
正如數(shù)學(xué)的發(fā)展是從1+1=2作為開始,我們也需要一些真理來支撐我們講下去。那么我們的核心依據(jù)就是:里氏替換——C#里,子類轉(zhuǎn)父類可以直接隱式轉(zhuǎn)換
就這么短,就完事了?對,記住就行!!!
Out/In 輸入輸出?
講到這里,我們繼續(xù)忽悠,out是啥?來個(gè)翻譯!不就是輸出嗎?in是啥,不就是輸入嗎?那么帶入一下,Out不就是返回值嗎,In不就是入?yún)帷D遣痪褪欠椒ǖ奶卣髅础#ㄏ燃僭O(shè),再假設(shè))
In:那么根據(jù)核心依據(jù),子類轉(zhuǎn)父類可以直接轉(zhuǎn),入?yún)⑷绻薅ㄊ荘erson類型,那么你給我限定為Student或者任意的Person類型的派生類,我都是可以接受的,因?yàn)槎际前踩模梢灾苯愚D(zhuǎn)換過來的。
這種從基類轉(zhuǎn)向派生類的兼容,就是所謂的逆變。說白了,我讓你給我一個(gè)人,你說不行,我給你找個(gè)學(xué)生,那肯定是滿足需求的。
Out:Out代表的是返回值,根據(jù)核心依據(jù),我返回的是Student類型,你說不行,你給我返回Person類型,那我不是笑開花了,我連Student都能返回,你讓我返回父類,那我不是直接轉(zhuǎn)就過去了,總歸是類型安全的。
這種從派生類轉(zhuǎn)向基類的兼容,就是所謂的協(xié)變。
說白了,我可以造個(gè)學(xué)生,結(jié)果你說給個(gè)人就行, 那不是so easy。
In示意圖
Out示意圖
證明
好了,我們說了這么多,至少證明下In/Out是代表的入?yún)⒑头祷刂蛋桑恐苯觭how you code: 當(dāng)Out作為返回值時(shí)的泛型沒有問題,但是入?yún)⒕蛨?bào)錯(cuò)了
當(dāng)In作為入?yún)r(shí)的泛型沒有問題,但是返回值就報(bào)錯(cuò)了
好了,這還需要再解釋嗎?最后我們總結(jié)下,逆變和協(xié)變就是讓方法有了泛型類型上的轉(zhuǎn)換能力,強(qiáng)化了方法的多態(tài)能力。
問題點(diǎn)
1、屬性為啥可以用逆變協(xié)變?
屬性不就是get/set方法。
2、為什么接口和委托可以用逆變協(xié)變,類不行?
拜托你找一下共同點(diǎn),接口和委托的共同點(diǎn),都是行為,也就是方法為核心。接口里不能有字段。這也印證了我說的逆變協(xié)變最終是為方法服務(wù)的。
之所以類不行,我大概理解是方法和實(shí)例是分開的,本身不和實(shí)例存儲(chǔ)在一起,也不是每個(gè)實(shí)例一份,如果逆變和協(xié)變可以服務(wù)類,那么會(huì)出現(xiàn)同樣的類型,但是每個(gè)實(shí)例內(nèi)部的同一個(gè)字段的類型都不一樣,這對于存儲(chǔ)和類型安全都是問題。
3、逆變和協(xié)變有啥用?
你...設(shè)計(jì)問題,我就有遇到,有時(shí)候用上能更加優(yōu)雅或者靈活的寫代碼吧,看你吧,少年。
轉(zhuǎn)自:BruceNeter
鏈接:cnblogs.com/qwqwQAQ/p/17622254.html
