引言:
本專題主要是承接上一個專題要繼續介紹泛型的其他内容,這裡就不多說了,就直接進入本專題的内容的。
一、類型推斷
在我們寫泛型代碼的時候經常有大量的"<"和">"符号,這樣有時候代碼一多,也難免會讓開發者在閱讀代碼過程中會覺得有點暈的,此時我們覺得暈的時候肯定就會這樣想:是不是能夠省掉一些"<" 和">"符号的呢?你有這種需求了, 當然微軟這位好人肯定也會幫你解決問題的,這樣就有了我們這部分的内容——類型推斷,意味着編譯器會在調用一個泛型方法時自動判斷要使用的類型,(這裡要注意的是:類型推斷隻使用于泛型方法,不适用于泛型類型),下面是示範代碼:
代碼中都有詳細的注釋,這裡就不解釋了。
二、類型限制
如果大家看了我的上一個專題的話,就應該會注意到我在實作泛型類的時候用到了where T : IComparable,在上一個專題并沒有和大家介紹這個是泛型的什麼用法,這個用法就是這個部分要講的類型限制,其實where T : IComparable這句代碼也很好了解的,猜猜也明白的(如果是我不知道的話,應該是猜類型參數T要滿足IComparable這個接口條件,因為Where就代表符合什麼條件的意思,然而真真意思也确實如此的)下面就讓我們具體看看泛型中的類型參數有哪幾種限制的。 首先,編譯泛型代碼時,C#編譯器肯定會對代碼進行分析,如果我們像下面定義一個泛型類型方法時,編譯器就會報錯:
如果像上面一樣定義泛型方法時,C#編譯器會提示錯誤資訊:“T”不包含“CompareTo”的定義,并且找不到可接受類型為“T”的第一個參數的擴充方法“CompareTo”。 這是因為此時類型參數T可以為任意類型,然而許多類型都沒有提供CompareTo方法,是以C#編譯器不能編譯上面的代碼,這時候我們(編譯器也是這麼想的)肯定會想——如果C#編譯器知道類型參數T有CompareTo方法的話,這樣上面的代碼就可以被C#編譯器驗證的時候通過,就不會出現編譯錯誤的(C#編譯器感覺很人性化的,都會按照人的思考方式去解決問題的,那是因為編譯器也是人開發出來的,當然會人性化的,因為開發人員當時就是這麼想的,是以就把邏輯寫到編譯器的實作中去了),這樣就讓我們想對類型參數作出一定限制,縮小類型參數所代表的類型數量——這就是我們類型限制的目的,進而也很自然的有了類型參數限制(這裡通過對遇到的分析然後去想辦法的解決的方式來引出類型限制的概念,主要是讓大家可以明白C#中的語言特性提出來都是有原因,并不是說微軟想提出來就提出來的,主要還是因為使用者會有這樣的需求,這樣的方式我覺得可以讓大家更加的明白C#語言特性的發展曆程,進而更加深入了解C#,從我前面的專題也看的出來我這樣介紹問題的方式的,不過這樣也是我個人的了解,希望這樣引入問題的方式對大家會有幫助,讓大家更好的了解C#語言特性,如果大家對于對于有任何意見和建議的話,都可以在留言中提出的,如果覺得好的話,也麻煩表示認可下)。是以上面的代碼可以指定一個類型限制,讓C#編譯器知道這個類型參數一定會有CompareTo方法的,這樣編譯器就不會報錯了,我們可以将上面代碼改為(代碼中T:IComparable<T>為類型參數T指定的類型實參都必須實作泛型IComparable接口):
類型限制就是用where 關鍵字來限制能指定類型實參的類型數量,如上面的where T:IComparable<T>語句。C# 中有4種限制可以使用,然而這4種限制的文法都差不多。(限制要放在泛型方法或泛型類型聲明的末尾,并且要使用Where關鍵字)
(1) 引用類型限制
表示形式為 T:class, 確定傳遞的類型實參必須是引用類型(注意限制的類型參數和類型本身沒有關系,意思就是說定義一個泛型結構體時,泛型類型一樣可以限制為引用類型,此時結構體類型本身是值類型,而類型參數限制為引用類型),可以為任何的類、接口、委托或數組等;但是注意不能指定下面特殊的引用類型:System.Object,System.Array,System.Delegate,System.MulticastDelegate,System.ValueType,System.Enum和System.Void.
如下面定義的泛型類:
上面代碼中類型參數T設定了引用類型限制,Where T:stream的意思就是告訴編譯器,傳入的類型實參必須是System.IO.Stream或者從Stream中派生的一個類型,如果一個類型參數沒有指定限制,則預設T為System.Object類型(相當于一個預設限制一樣,就想每個類如果沒有指定構造函數就會有預設的無參數構造函數,如果指定了帶參數的構造函數,編譯器就不會生成一個預設的構造函數)。然而,如果我們在代碼中顯示指定System.Object限制時,此時會編譯器會報錯:限制不能是特殊類“object”(這裡大家可以自己試試看的)
(2)值類型限制
表示形式為T:struct,確定傳遞的類型實參時值類型,其中包括枚舉,但是可空類型排除,(可空類型将會在後面專題有所介紹),如下面的示例:
在上面代碼中,new T()是可以通過編譯的,因為T 是一個值類型,而所有值類型都有一個公共的無參構造函數,然而,如果T不限制,或限制為引用類型時,此時上面的代碼就會報錯,因為有的引用類型沒有公共的無參構造函數的。
(3)構造函數類型限制
表示形式為T:new(),如果類型參數有多個限制時,此限制必須為最後指定。確定指定的類型實參有一個公共無參構造函數的非抽象類型,這适用于:所有值類型;所有非靜态、非抽象、沒有顯示聲明的構造函數的類(前面括号中已經說了,如果顯示聲明帶參數的構造函數,則編譯器就不會為類生成一個預設的無參構造函數,大家可以通過IL反彙程式設計式檢視下的,這裡就不貼圖了);顯示聲明了一個公共無參構造函數的所有非抽象類。(注意: 如果同時指定構造器限制和struct限制,C#編譯器會認為這是一個錯誤,因為這樣的指定是多餘的,所有值類型都隐式提供一個無參公共構造函數,就如定義接口指定通路類型為public一樣,編譯器也會報錯,因為接口一定是public的,這樣的做隻多餘的,是以會報錯。)
(4)轉換類型限制
表示形式為 T:基類名 (確定指定的類型實參必須是基類或派生自基類的子類)或T:接口名(確定指定的類型實參必須是接口或實作了該接口的類) 或T:U(為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數)。轉換限制的例子如下:
聲明
已構造類型的例子
Class Sample<T> where T: Stream
Sample<Stream>有效的
Sample<string>無效的
Class Sample<T> where T: IDisposable
Sample<Stream >有效的
Sample<StringBuilder>無效的
Class Sample<T,U> where T: U
Sample<Stream,IDispsable>有效的
Sample<string,IDisposable>無效的
(5)組合限制(第五種限制就是前面的4種限制的組合)
将多個不同種類的限制合并在一起的情況就是組合限制了。(注意,沒有任何類型即時引用類型又是值類型的,是以引用限制和值限制不能同時使用)如果存在多個轉換類型限制時,如果其中一個是類,則類必須放在接口的前面。不同的類型參數可以有不同的限制,但是他們分别要由一個單獨的where關鍵字。下面看一些有效和無效的例子來讓大家加深印象:
有效:
class Sample<T> where T:class, IDisposable, new();
class Sample<T,U> where T:class where U: struct
無效的:
class Sample<T> where T: class, struct (沒有任何類型即時引用類型又是值類型的,是以為無效的)
class Sample<T> where T: Stream, class (引用類型限制應該為第一個限制,放在最前面,是以為無效的)
class Sample<T> where T: new(), Stream (構造函數限制必須放在最後面,是以為無效)
class Sample<T> where T: IDisposable, Stream(類必須放在接口前面,是以為無效的)
class Sample<T,U> where T: struct where U:class, T (類型形參“T”具有“struct”限制,是以“T”不能用作“U”的限制,是以為無效的)
class Sample<T,U> where T:Stream, U:IDisposable(不同的類型參數可以有不同的限制,但是他們分别要由一個單獨的where關鍵字,是以為無效的)
三、利用反射調用泛型方法
上面代碼在調用泛型方法時傳入的兩個實參都是null,傳入第一個為null是因為調用的是一個靜态方法, 第二null是因為調用的方法是個無參的方法。 運作結果截圖(結果是輸出出 類型實參的類型,結果和我們預期的一樣):
四、小結
說到這裡泛型的内容都已經介紹完了,本系列用了三個專題來介紹泛型,文章内容都基本采用提出疑問(為什麼有泛型)到解釋疑問,再到深入了解泛型的方式(個人認為這樣的講解方式不錯的,如果大家有更好的講解方式可以在下面留言給我),希望這種方式可以讓大家知道泛型的起源,進而更好的了解泛型。後面一專題将和大家介紹了C#4.0中對泛型的改進——泛型的可變性。
<a href="http://down.51cto.com/data/2361735" target="_blank">附件:http://down.51cto.com/data/2361735</a>
本文轉自LearningHard 51CTO部落格,原文連結:http://blog.51cto.com/learninghard/1054399,如需轉載請自行聯系原作者