日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

c#: 协变和逆变深度解析

發(fā)布時(shí)間:2023/12/4 C# 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c#: 协变和逆变深度解析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

環(huán)境:

window 10
.netcore 3.1
vs2019 16.5.1

一、為什么要有協(xié)變?
首先看下面的代碼:

還有下面的:

其實(shí)上面報(bào)錯(cuò)的是同一個(gè)問題,就是你無法用List<Fruit>指向List<Apple>!
我們的疑問在于,明明是一個(gè)盛放蘋果的箱子,我們說它可以盛放水果怎么了???
下面我來說一下原因:

  • 首先,不能根據(jù)這個(gè)類的用途去判斷,因?yàn)槟銦o法保證List這個(gè)類一定是集合(List當(dāng)然是集合,但如果是Person<T>呢,它是做什么的?只是盛放東西嗎?)。
    其次,Apple繼承自Fruit沒錯(cuò),但List<Apple>和List<Fruit>壓根就沒有繼承的說法,它們是不同的類型(泛型參數(shù)類型不同也是不同的類型):

  • Console.WriteLine(typeof(List<Apple>) == typeof(List<Fruit>));輸出為:false

所以,我們用List<Fruit>去表示List<Apple>引發(fā)報(bào)錯(cuò)很正常!!!
但是,從我們程序員角度來說,這樣肯定不方便,那么有沒有解決辦法呢?
答案:有,它就是協(xié)變!


二、什么是協(xié)變?
首先,明確一下目的:我們想讓List<Fruit> list = new List<Apple>();這類代碼成立!(這行代碼肯定不成立,我說的是這類代碼)
想要達(dá)到我們的目的,肯定是要有規(guī)則的:

必須使用接口進(jìn)行指向,不能使用類:

比如說:我們只能這么寫IList<Fruit> list = new List<Apple>();(雖然這樣寫也報(bào)錯(cuò)),不能夠這么寫List<Fruit> list = new List<Apple>();

為什么不能使用類?因?yàn)轭惱锩鏍砍兜降膬?nèi)容比較多,而下一條規(guī)則就說了:方法的入?yún)⒉荒苁褂梅盒蛥?shù),所以為了盡量把這種約束的范圍變小一點(diǎn),我們也應(yīng)該在接口上加規(guī)則約束而不是直接在類上(這一點(diǎn)是我猜的)。


這個(gè)接口的泛型參數(shù)只能用來做接口內(nèi)方法的返回值,不能用作接口內(nèi)方法的參數(shù)(在泛型參數(shù)前加out關(guān)鍵字實(shí)現(xiàn)):

這里從兩方面說:
1.允許這個(gè)泛型參數(shù)做返回值:比如定義接口ITest<out T>,允許T作為接口內(nèi)方法MethodA的返回值(T MethodA();)。在使用的時(shí)候,你用ITest<Fruit>指向ITest<Apple>,那么當(dāng)調(diào)用ITest<Fruit>的方法MethodA的時(shí)候你得到的返回類型聲明是Fruit,實(shí)際上你得到的返回類型是Apple,所以一點(diǎn)問題沒有。
2.禁止這個(gè)泛型參數(shù)做方法的入?yún)?#xff1a;比如定義接口ITest<out T>,允許T作為接口內(nèi)方法MethodA的入?yún)?#xff08;void MethodA(T t);)。在使用的時(shí)候,你用ITest<Fruit>指向ITest<Apple>,那么當(dāng)調(diào)用ITest<Fruit>的方法MethodA的時(shí)候你看到這個(gè)方法要求傳入一個(gè)Fruit,所以你可能傳一個(gè)orange(橙子,也繼承了Fruit)進(jìn)去,但人家實(shí)際上是ITest<Apple>,要求傳入的是Apple,這樣肯定說不通!所以泛型參數(shù)禁止做方法的入?yún)?#xff01;

上面說了規(guī)則,那么下面來一個(gè)實(shí)例:

可以看到,我們按照規(guī)則在ITest的泛型參數(shù)T上加了out后,整個(gè)程序腰不酸了、腿不疼了。
事實(shí)上,微軟在集合的定義上已經(jīng)考慮到了這一點(diǎn),看一下IEnumerable的定義:

所以,我們像下面這樣寫也沒有錯(cuò):

講到這里,我們可以說一下什么是協(xié)變了:
假如有兩個(gè)類:A和AA,其中AA繼承自A,如果此時(shí)有一個(gè)泛型接口IC<out T>,那么可以認(rèn)為IC<A>能指向IC<AA>,即:IC<AA>和IC<A>的關(guān)系看著像AA和A的關(guān)系一樣(只是看著像,并且能單方向轉(zhuǎn)換,但不是繼承!!!)。


三、什么是逆變?
逆變和協(xié)變是相對(duì)的,具體來說:

逆變的目的是:讓List<Apple> test = new List<Fruit>();這類代碼成立!(這行代碼肯定報(bào)錯(cuò),我說的是這類代碼)
你一定認(rèn)為這瘋了,“說一個(gè)盛放水果的箱子盛放的是蘋果”肯定不對(duì)。
但是我們看下面的實(shí)例:

上圖中的代碼是不是顛覆了你的認(rèn)知?
好吧,這就是逆變:一個(gè)可以讓你用ITest<Apple>去指向Test<Fruit>()的存在!
這里還是再說一下逆變的規(guī)則:

必須使用接口進(jìn)行指向,不能使用類:

這一點(diǎn)和協(xié)變是一樣的。


這個(gè)接口的泛型參數(shù)只能用來做接口內(nèi)方法的入?yún)?#xff0c;不能用作接口內(nèi)方法的返回值(在泛型參數(shù)前加in關(guān)鍵字實(shí)現(xiàn)):

這里從兩方面說:
1.允許這個(gè)泛型參數(shù)做方法的入?yún)?#xff1a;比如定義接口ITest<in T>,允許T作為接口內(nèi)方法MethodA的入?yún)?#xff08;void SetValue(T t);)。在使用的時(shí)候,你用ITest<Apple>指向ITest<Fruit>,那么當(dāng)你調(diào)用ITest<Apple>的方法MethodA的時(shí)候你看到這個(gè)方法要求傳入一個(gè)Apple,實(shí)際上人家是ITest<Fruit>,人家要求傳入的是Fruit,所以這里一點(diǎn)問題沒有。
2.禁止這個(gè)泛型參數(shù)做方法的返回值:比如定義接口ITest<in T>,允許T作為接口內(nèi)方法MethodA的返回值(T GetValue();)。在使用的時(shí)候,你用ITest<Apple>指向ITest<Fruit>,那么當(dāng)調(diào)用ITest<Apple>的方法MethodA的時(shí)候你得到的返回類型聲明是Apple,但實(shí)際上人家是ITest<Fruit>,所以返回的是一個(gè)orange(橙子,也繼承了Fruit)也說不定,所以你用Apple去接收這個(gè)返回值肯定不行的,所以泛型參數(shù)禁止做方法的返回值!

四、委托內(nèi)的協(xié)變和逆變
委托中的泛型參數(shù)是天然就可以支持協(xié)變或逆變中的一種的!
對(duì)這句話的理解如下:

如果你這么定義委托:public delegate T GetValue<T>();,那么它天然支持協(xié)變(因?yàn)門只用來聲明返回值),如下代碼:

如果你這么定義委托:public delegate void SetValue<T>(T t);,那么它天然支持逆變(因?yàn)門只用來做入?yún)?#xff09;,如下代碼:

如果你這么定義委托,它既不支持協(xié)變,也不支持逆變:public delegate T Deal<T>(T t);(因?yàn)門即用來做入?yún)⒁灿脕碜龇祷刂?#xff09;,如下代碼:

其實(shí),在委托中為了更好的表示泛型參數(shù)是支持協(xié)變還是逆變,最好是定義的時(shí)候就用out或in參數(shù)進(jìn)行聲明,比如:
public delegate T GetValue<out T>();//支持協(xié)變
public delegate void SetValue<in T>(T t);//支持逆變
微軟在Func、Action系列委托中已經(jīng)為我們做了示范:

總結(jié)

以上是生活随笔為你收集整理的c#: 协变和逆变深度解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。