C#4.0中有一個新特性:協變與逆變。可能不少人在開發過程當中不經常使用到,可是深刻的瞭解他們,確定是有好處的。spa
協變和逆變體如今泛型的接口和委託上面,也就是對泛型參數的聲明,能夠聲明爲協變,或者逆變。什麼?泛型的參數還能聲明?對,若是有了參數的聲明,則該泛型接口或者委託稱爲「變體」。code
List<汽車> 一羣汽車 = new List<汽車>(); List<車子> 一羣車子 = 一羣汽車;
顯然,上面那段代碼是會報錯的, 雖然汽車繼承於車子,能夠隱士轉換爲車子,可是List<汽車>並不繼承於List<車子>,因此上面的轉換,是行不通的。blog
IEnumerable<汽車> 一羣汽車 = new List<汽車>(); IEnumerable<車子> 一羣車子 = 一羣汽車;
然而這樣倒是能夠的。那麼IEnumerable接口有什麼不一樣呢,咱們且看編譯器的提示:繼承
咱們能夠看到,泛型參數的,用了一個「out」關鍵字做爲聲明。看來,關鍵是這個在起做用了。接口
「協變」是指可以使用與原始指定的派生類型相比,派生程度更大的類型。 開發
「逆變」則是指可以使用派生程度更小的類型。逆變,逆於常規的變。get
協變和逆變,使用「out」,和「in」兩個關鍵字。可是隻能用在接口和委託上面,對泛型的類型進行聲明編譯器
當聲明爲「out」時,表明它是用來返回的,只能做爲結果返回,中途不能更改。string
當聲明爲"in"時,表明它是用來輸入的,只能做爲參數輸入,不能被返回。it
回到上面的例子,正由於「IEnumerable」接口聲明瞭out,因此,表明參數T只能被返回,中途不會被修改,因此,IEnumerable<車子> 一羣車子 = 一羣汽車; 這樣的強制轉換
是合法的,IL中其實是做了強制轉換的。
IEnumerable是NET中自帶的,其他還有以下接口和委託:
接口:
IQueryable<out T> IEnumerator<out T> IGrouping<out TKey,out TElement> 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>
此外,咱們本身定義泛型接口的時候也能夠使用協變和逆變,咱們不妨來看一個示例,來體現協變的特徵
interface 接口<out T> { T 屬性 { get; set; } }
我定義一個接口,一個具備get和set訪問器的屬性,然而,編譯是報錯的,提示:變體無效: 類型參數「T」必須爲對於「test.接口<T>.屬性」有效的 固定式。「T」爲 協變。
正由於我聲明瞭T爲協變,因此,T只能被返回,不容許被修改,因此,若是去掉「set」訪問器,才能夠編譯經過。
一樣,若是我在「接口」中聲明一個方法
void 方法(T t);
一樣是會報錯的,T被聲明瞭協變,「方法(T t)」的存在就不可取。
class Program { static void Main(string[] args) { 接口<汽車> 一羣汽車 = new 類<汽車>(); 接口<車子> 一羣車子 = 一羣汽車; } } interface 接口<out T> { T 屬性 { get; } } class 類<T> : 接口<T> { public T 屬性 { get { return default(T); } } }
上面的代碼是能夠編譯經過的,由於泛型接口「接口」聲明瞭協變,因此「接口<車子> 一羣車子 = 一羣汽車;」是能夠強制轉換成功的,看吧,咱們本身聲明的一樣能夠實現目的。
若是我把以上的代碼,把「out」改爲「in」呢? 顯然不行,由於聲明「in」規定了T不能被返回,編譯沒法經過的。
然而下面的代碼是正確的:
interface 接口<in T> { void 方法(T t); } class 類<T> : 接口<T> { public void 方法(T t) { } }
聲明「in」不容許被返回,可是能夠進行更改。
接着看:
static void Main(string[] args) { 接口<車子> 一羣車子 = new 類<車子>(); 接口<汽車> 一羣汽車 = 一羣車子; }
啊,這怎麼也能夠啊,「車子」是父類,「汽車」是子類,汽車轉換爲車子正常,車子轉換爲汽車,這樣也行?
其實「車子」也好,「汽車」也好,在這裏都只是泛型參數,並非他們倆之間的轉換,這個基礎的概念必須明白,別繞進去了。
這就是逆變。由於「接口」聲明瞭「in」關鍵字,聲明爲逆變,讓參數去接受一個相對更「弱「的類型,實際上是讓一個參數的類型,更加具體化,更明確化的一個過程。