泛型在.NET 2.0中正式的引入。在使用泛型的過程中,聯系上面向對象的繼承性。往往很容易想當然敲出類似以下代碼
List<Animal> animalLst=new List<Dog>;
顯然這樣編譯是不通過的。雖然Dog和Animal之間有繼承性,但是List<Animal>和List<Dog>這兩個類之間並沒有繼承性。如果要解決這樣的問題,用上協變與抗變(逆變),它們統稱為變體。是.NET 4.0引入的新特性,但是早在.NET 2.0就引入了。
變體適用於泛型接口與泛型委托,在我們聲明泛型借口或泛型委托時,加上out或in關鍵字就能實現。
- out是用於協變,這就好比子類通過隱式轉換成基類的情形,這么平淡的轉換,與“協”的感覺類似;
- in是用於抗變,類似於基類強制轉換成子類,帶有一些負面的感覺的,於是“逆”或者“抗”關聯上了。
兩者可以說得上是用於整條類的繼承鏈上轉換工作的,但是方向不同。
那么先來看看協變
delegate T AnimalAction1<out T>(); interface IProduce<out T> { T TypeIns { get; } T CreteNewTypeIns(); }
out這個關鍵字使人聯想到“輸出”,既然是出的話,那么協變的類型只能用於方法的返回值或者是屬性的get,如果變量作為方法的參數(即使是帶out關鍵字的參數)和屬性的set,那么編譯會報錯。那么在FCL里面是協變的接口和委托如下
IQueryable<out T> IEnumerator<out T> IGrouping<out TKey,out TElement> Converter<in TInput,out TOutput>
看上去都是返回泛型T的。
再看看抗變
delegate void AnimalBark<in T>(T animal); interface IRunnable<in T> { T TypeIns { set; } void CanRun(T t); }
既然與協變相反,那么它就代表着“輸入”,抗變類型就只能用於方法的參數和屬性的set。在FCL里抗變的接口和委托如下
IComparer<in T> IEqualityComparer<in T> IComparable<in T> System.Action<in T> System.Func<Out Tresult> Predicate<in T> Comparison<in T> Converter<in TInput,out TOutput>
可是變體不適用於上面的List<T>,因為這個泛型類在聲明的時候並沒有用上out關鍵字,就算是用上了也不現實,因為對於List<T>這個這個泛型類來說,它本身就存在着輸入與輸出兩種行為,Add,Remove等方法就要利用到參數的輸入,同時它本身又能通過索引器來獲取某個所以值下的元素,既有out又有in的泛型相互矛盾,定義不出來。
本文因看見某位園有寫了一篇相同主題的文章而寫的,想着不久前某位同事也叫我探討過變體,當時我看了不久就忘了,這回看到那位園友的博文,我想想我還是也記錄一下吧!不放博客園首頁了。
