C#中協變與抗變(逆變)


  泛型在.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的泛型相互矛盾,定義不出來。
  本文因看見某位園有寫了一篇相同主題的文章而寫的,想着不久前某位同事也叫我探討過變體,當時我看了不久就忘了,這回看到那位園友的博文,我想想我還是也記錄一下吧!不放博客園首頁了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM