亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > .NET > 正文

詳解.NET 4.0中的泛型協變(covariant)和反變(contravariant)

2024-07-10 13:29:01
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了詳解.NET 4.0中的泛型協變(covariant)和反變(contravariant),本文講解了協變和反變的背景知識、.NET 4.0引入的泛型協變、反變性、協變和反變的相互作用等內容,需要的朋友可以參考下
 

隨Visual Studio 2010 CTP亮相的C#4和VB10,雖然在支持語言新特性方面走了相當不一樣的兩條路:C#著重增加后期綁定和與動態語言相容的若干特性,VB10著重簡化語言和提高抽象能力;但是兩者都增加了一項功能:泛型類型的協變(covariant)和反變(contravariant)。許多人對其了解可能僅限于增加的in/out關鍵字,而對其諸多特性有所不知。下面我們就對此進行一些詳細的解釋,幫助大家正確使用該特性。

背景知識:協變和反變

很多人可能不不能很好地理解這些來自于物理和數學的名詞。我們無需去了解他們的數學定義,但是至少應該能分清協變和反變。實際上這個詞來源于類型和類型之間的綁定。我們從數組開始理解。數組其實就是一種和具體類型之間發生綁定的類型。數組類型Int32[]就對應于Int32這個原本的類型。任何類型T都有其對應的數組類型T[]。那么我們的問題就來了,如果兩個類型T和U之間存在一種安全的隱式轉換,那么對應的數組類型T[]和U[]之間是否也存在這種轉換呢?這就牽扯到了將原本類型上存在的類型轉換映射到他們的數組類型上的能力,這種能力就稱為“可變性(Variance)”。在.NET世界中,唯一允許可變性的類型轉換就是由繼承關系帶來的“子類引用->父類引用”轉換。舉個例子,就是String類型繼承自Object類型,所以任何String的引用都可以安全地轉換為Object引用。我們發現String[]數組類型的引用也繼承了這種轉換能力,它可以轉換成Object[]數組類型的引用,數組這種與原始類型轉換方向相同的可變性就稱作協變(covariant)。

由于數組不支持反變性,我們無法用數組的例子來解釋反變性,所以我們現在就來看看泛型接口和泛型委托的可變性。假設有這樣兩個類型:TSub是TParent的子類,顯然TSub型引用是可以安全轉換為TParent型引用的。如果一個泛型接口IFoo<T>,IFoo<TSub>可以轉換為IFoo<TParent>的話,我們稱這個過程為協變,而且說這個泛型接口支持對T的協變。而如果一個泛型接口IBar<T>,IBar<TParent>可以轉換為T<TSub>的話,我們稱這個過程為反變(contravariant),而且說這個接口支持對T的反變。因此很好理解,如果一個可變性和子類到父類轉換的方向一樣,就稱作協變;而如果和子類到父類的轉換方向相反,就叫反變性。你記住了嗎?

.NET 4.0引入的泛型協變、反變性

剛才我們講解概念的時候已經用了泛型接口的協變和反變,但在.NET 4.0之前,無論C#還是VB里都不支持泛型的這種可變性。不過它們都支持委托參數類型的協變和反變。由于委托參數類型的可變性理解起來抽象度較高,所以我們這里不準備討論。已經完全能夠理解這些概念的讀者自己想必能夠自己去理解委托參數類型的可變性。在.NET 4.0之前為什么不允許IFoo<T>進行協變或反變呢?因為對接口來講,T這個類型參數既可以用于方法參數,也可以用于方法返回值。設想這樣的接口

復制代碼代碼如下:

Interface IFoo(Of T)
    Sub Method1(ByVal param As T)
    Function Method2() As T
End Interface
interface IFoo<T>
{
    void Method1(T param);
    T Method2();
}

如果我們允許協變,從IFoo<TSub>到IFoo<TParent>轉換,那么IFoo.Method1(TSub)就會變成IFoo.Method1(TParent)。我們都知道TParent是不能安全轉換成TSub的,所以Method1這個方法就會變得不安全。同樣,如果我們允許反變IFoo<TParent>到IFoo<TSub>,則TParent IFoo.Method2()方法就會變成TSub IFoo.Method2(),原本返回的TParent引用未必能夠轉換成TSub的引用,Method2的調用將是不安全的。有此可見,在沒有額外機制的限制下,接口進行協變或反變都是類型不安全的。.NET 4.0改進了什么呢?它允許在類型參數的聲明時增加一個額外的描述,以確定這個類型參數的使用范圍。我們看到,如果一個類型參數僅僅能用于函數的返回值,那么這個類型參數就對協變相容。而相反,一個類型參數如果僅能用于方法參數,那么這個類型參數就對反變相容。如下所示:
復制代碼代碼如下:

Interface ICo(Of Out T)
    Function Method() As T
End Interface
 
Interface IContra(Of In T)
    Sub Method(ByVal param As T)
End Interface
interface ICo<out T>
{
    T Method();
}
 
interface IContra<in T>
{
    void Method(T param);
}

可以看到C#4和VB10都提供了大同小異的語法,用Out來描述僅能作為返回值的類型參數,用In來描述僅能作為方法參數的類型參數。一個接口可以帶多個類型參數,這些參數可以既有In也有Out,因此我們不能簡單地說一個接口支持協變還是反變,只能說一個接口對某個具體的類型參數支持協變或反變。比如若有IBar<in T1, out T2>這樣的接口,則它對T1支持反變而對T2支持協變。舉個例子來說,IBar<object, string>能夠轉換成IBar<string, object>,這里既有協變又有反變。

 

在.NET Framework中,許多接口都僅僅將類型參數用于參數或返回值。為了使用方便,在.NET Framework 4.0里這些接口將重新聲明為允許協變或反變的版本。例如IComparable<T>就可以重新聲明成IComparable<in T>,而IEnumerable<T>則可以重新聲明為IEnumerable<out T>。不過某些接口IList<T>是不能聲明為in或out的,因此也就無法支持協變或反變。

下面提起幾個泛型協變和反變容易忽略的注意事項:

1.僅有泛型接口和泛型委托支持對類型參數的可變性,泛型類或泛型方法是不支持的。
2.值類型不參與協變或反變,IFoo<int>永遠無法變成IFoo<object>,不管有無聲明out。因為.NET泛型,每個值類型會生成專屬的封閉構造類型,與引用類型版本不兼容。
3.聲明屬性時要注意,可讀寫的屬性會將類型同時用于參數和返回值。因此只有只讀屬性才允許使用out類型參數,只寫屬性能夠使用in參數。

協變和反變的相互作用

這是一個相當有趣的話題,我們先來看一個例子:

復制代碼代碼如下:

Interface IFoo(Of In T)
 
End Interface
 
Interface IBar(Of In T)
    Sub Test(ByVal foo As IFoo(Of T)) '對嗎?
End Interface
interface IFoo<in T>
{
 
}
 
interface IBar<in T>
{
    void Test(IFoo<T> foo); //對嗎?
}

你能看出上述代碼有什么問題嗎?我聲明了in T,然后將他用于方法的參數了,一切正常。但出乎你意料的是,這段代碼是無法編譯通過的!反而是這樣的代碼通過了編譯:
復制代碼代碼如下:

Interface IFoo(Of In T)
 
End Interface
 
Interface IBar(Of Out T)
    Sub Test(ByVal foo As IFoo(Of T))
End Interface
interface IFoo<in T>
{
 
}
 
interface IBar<out T>
{
    void Test(IFoo<T> foo);
}

 

什么?明明是out參數,我們卻要將其用于方法的參數才合法?初看起來的確會有一些驚奇。我們需要費一些周折來理解這個問題?,F在我們考慮IBar<string>,它應該能夠協變成IBar<object>,因為string是object的子類。因此IBar.Test(IFoo<string>)也就協變成了IBar.Test(IFoo<object>)。當我們調用這個協變后方法時,將會傳入一個IFoo<object>作為參數。想一想,這個方法是從IBar.Test(IFoo<string>)協變來的,所以參數IFoo<object>必須能夠變成IFoo<string>才能滿足原函數的需要。這里對IFoo<object>的要求是它能夠反變成IFoo<string>!而不是協變。也就是說,如果一個接口需要對T協變,那么這個接口所有方法的參數類型必須支持對T的反變。同理我們也可以看出,如果接口要支持對T反變,那么接口中方法的參數類型都必須支持對T協變才行。這就是方法參數的協變-反變互換原則。所以,我們并不能簡單地說out參數只能用于返回值,它確實只能直接用于聲明返回值類型,但是只要一個支持反變的類型協助,out類型參數就也可以用于參數類型!換句話說,in參數除了直接聲明方法參數之外,也僅能借助支持協變的類型才能用于方法參數,僅支持對T反變的類型作為方法參數也是不允許的。要想深刻理解這一概念,第一次看可能會有點繞,建議有條件的情況下多進行一些實驗。

剛才提到了方法參數上協變和反變的相互影響。那么方法的返回值會不會有同樣的問題呢?我們看如下代碼:

復制代碼代碼如下:

Interface IFooCo(Of Out T)
 
End Interface
 
Interface IFooContra(Of In T)
 
End Interface
 
Interface IBar(Of Out T1, In T2)
    Function Test1() As IFooCo(Of T1)
    Function Test2() As IFooContra(Of T2)
End Interface
interface IFooCo<out T>
{
}
 
interface IFooContra<in T>
{
}
 
interface IBar<out T1, in T2>
{
    IFooCo<T1> Test1();
    IFooContra<T2> Test2();
}

 

我們看到和剛剛正好相反,如果一個接口需要對T進行協變或反變,那么這個接口所有方法的返回值類型必須支持對T同樣方向的協變或反變。這就是方法返回值的協變-反變一致原則。也就是說,即使in參數也可以用于方法的返回值類型,只要借助一個可以反變的類型作為橋梁即可。如果對這個過程還不是特別清楚,建議也是寫一些代碼來進行實驗。至此我們發現協變和反變有許多有趣的特性,以至于在代碼里in和out都不像他們字面意思那么好理解。當你看到in參數出現在返回值類型,out參數出現在參數類型時,千萬別暈倒,用本文的知識即可破解其中奧妙。

總結

經過本文的講解,大家應該已經初步了解的協變和反變的含義,能夠分清協變、反變的過程。我們還討論了.NET 4.0支持泛型接口、委托的協變和反變的新功能和新語法。最后我們還套了論的協變、反變與函數參數、返回值的相互作用原理,以及由此產生的奇妙寫法。我希望大家看了我的文章后,能夠將這些知識用于泛型程序設計當中,正確運用.NET 4.0的新增功能。祝大家使用愉快!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品99久久久久久www| 亚洲白拍色综合图区| 欧美精品videosex极品1| 成人午夜在线视频一区| 国产精品一区二区三区在线播放| 欧美日韩ab片| 伊人精品在线观看| 97国产真实伦对白精彩视频8| 亚洲另类欧美自拍| 国产精品美女www爽爽爽视频| 日韩av在线免费播放| 亚洲tv在线观看| 日韩二区三区在线| 成人观看高清在线观看免费| 国内免费久久久久久久久久久| 成人黄色大片在线免费观看| 亚洲人成电影网站色| 最好看的2019年中文视频| 欧美夜福利tv在线| 欧美激情视频给我| 欧美裸体男粗大视频在线观看| 91av成人在线| 亚洲精品美女久久久久| 国产精品美女免费视频| 欧美午夜www高清视频| 欧美专区中文字幕| 91夜夜未满十八勿入爽爽影院| 欧美色图在线视频| 亚洲欧美日韩国产成人| 日韩成人中文字幕| 久久久久久久久网站| 亚洲国产精品人久久电影| 精品国产一区久久久| 久久视频国产精品免费视频在线| 日韩一二三在线视频播| 亚洲**2019国产| 欧美大成色www永久网站婷| 日av在线播放中文不卡| 欧美理论电影在线播放| 亚洲美女在线视频| 亚洲成av人片在线观看香蕉| 一个人www欧美| 国产精品高潮呻吟视频| 国产成人久久久精品一区| 91精品久久久久久久久中文字幕| 国产精品视频久久久久| 中文字幕亚洲综合久久筱田步美| 国产精品久久久久999| 亚洲香蕉在线观看| xxxx性欧美| 国产欧美精品久久久| 欧美成人一二三| 神马久久桃色视频| 日韩av免费一区| 欧美国产高跟鞋裸体秀xxxhd| 欧美中文在线观看| 欧美性xxxxxxxxx| 欧美激情区在线播放| 久久久日本电影| 日韩av123| 日韩人在线观看| 一本色道久久88亚洲综合88| 国产欧美一区二区三区视频| 成人做爽爽免费视频| 国产在线拍偷自揄拍精品| 成人精品在线视频| 57pao国产成人免费| 亚洲色图17p| 国产精品久久久久久搜索| 亚洲成人xxx| 欧美日韩另类视频| 亚洲少妇激情视频| 亚洲国产精品成人一区二区| 国产98色在线| 久久中文久久字幕| 7777kkkk成人观看| 久久亚洲国产精品| 亚洲国产精品福利| 欧美激情va永久在线播放| 日韩中文在线中文网在线观看| 狠狠躁夜夜躁人人爽天天天天97| 欧美成人精品在线观看| 久久久久久中文| 欧美激情一区二区三区高清视频| 国产欧美一区二区三区久久| 欧美性色xo影院| 日韩成人网免费视频| 欧美xxxx综合视频| 精品久久久av| 色妞色视频一区二区三区四区| 成人欧美一区二区三区黑人孕妇| 欧美xxxx18性欧美| 亚洲欧美另类人妖| 在线观看久久av| 精品亚洲永久免费精品| 8090成年在线看片午夜| 国产精品91久久久久久| 亚洲精品456在线播放狼人| 亚洲国产成人久久综合| 亚洲а∨天堂久久精品喷水| 国产一区二区美女视频| 久久久国产视频91| 神马久久久久久| 欧美精品videos性欧美| 欧美激情va永久在线播放| 国产日韩精品综合网站| 欧美午夜片在线免费观看| 欧美中文字幕在线播放| 97国产精品视频| 亚洲精品电影在线观看| 亚洲一区二区三区sesese| 精品亚洲夜色av98在线观看| 欧美日韩国产成人| 欧美夫妻性生活视频| 一区二区中文字幕| 国产精品伦子伦免费视频| 国产成人精品在线观看| 黑人精品xxx一区| 精品日韩中文字幕| 日韩av片永久免费网站| 69国产精品成人在线播放| 国产精品免费网站| 精品一区二区三区电影| 亚洲欧美国内爽妇网| 欧美亚洲一区在线| 国产综合在线看| 日韩精品免费在线视频| 亚洲成年网站在线观看| 久久精品成人欧美大片古装| 国产偷国产偷亚洲清高网站| 欧美激情精品久久久久久| 国产精自产拍久久久久久蜜| 亚洲开心激情网| 日韩精品视频免费在线观看| 中文字幕亚洲综合久久| 亚洲精品久久久久久久久久久久久| 91精品视频网站| 国产午夜精品美女视频明星a级| 日本久久久久久久久| 中文精品99久久国产香蕉| 国产精品免费一区二区三区都可以| 欧美日韩在线视频一区二区| 欧洲美女免费图片一区| 少妇精69xxtheporn| 国精产品一区一区三区有限在线| 日韩av免费看| 人妖精品videosex性欧美| 日韩专区中文字幕| 久久人人97超碰精品888| 欧美精品video| 日韩成人中文电影| 欧美一区二粉嫩精品国产一线天| 国产精品久久久久77777| 国产成人在线精品| 国产成人精品久久亚洲高清不卡| 国产精品久久久久久久久久久新郎| 一区二区三区国产视频| 亚洲男人第一网站| 国产精品久久久久久五月尺| 精品一区二区电影| 日韩精品久久久久久福利| 一区二区三区回区在观看免费视频| 国产97在线|亚洲| 久久久久久国产免费|