程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#4.0泛型的協變,逆變深入剖析

C#4.0泛型的協變,逆變深入剖析

編輯:C#入門知識

C#4.0中有一個新特性:協變與逆變。可能很多人在開發過程中不常用到,但是深入的了解他們,肯定是有好處的。        協變和逆變體現在泛型的接口和委托上面,也就是對泛型參數的聲明,可以聲明為協變,或者逆變。什麼?泛型的參數還能聲明?對,如果有了參數的聲明,則該泛型接口或者委托稱為“變體”。   List<汽車> 一群汽車 = new List<汽車>(); List<車子> 一群車子 = 一群汽車;      顯然,上面那段代碼是會報錯的, 雖然汽車繼承於車子,可以隱士轉換為車子,但是List<汽車>並不繼承於List<車子>,所以上面的轉換,是行不通的。   IEnumerable<汽車> 一群汽車 = new List<汽車>(); IEnumerable<車子> 一群車子 = 一群汽車; 然而這樣卻是可以的。那麼IEnumerable接口有什麼不同呢,我們且看編譯器的提示:       我們可以看到,泛型參數的,用了一個“out”關鍵字作為聲明。看來,關鍵是這個在起作用了。    “協變”是指能夠使用與原始指定的派生類型相比,派生程度更大的類型。     “逆變”則是指能夠使用派生程度更小的類型。逆變,逆於常規的變。   協變和逆變,使用“out”,和“in”兩個關鍵字。但是只能用在接口和委托上面,對泛型的類型進行聲明   當聲明為“out”時,代表它是用來返回的,只能作為結果返回,中途不能更改。   當聲明為"in"時,代表它是用來輸入的,只能作為參數輸入,不能被返回。   回到上面的例子,正因為“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”關鍵字,聲明為逆變,讓參數去接受一個相對更“弱“的類型,其實是讓一個參數的類型,更加具體化,更明確化的一個過程。        

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved