上一篇對.NET中的泛型進行了詳細的介紹以及使用泛型的好處是什么,這篇將更加深入的去了解泛型的其他的知識點,重頭戲.
創新互聯從2013年成立,是專業互聯網技術服務公司,擁有項目成都網站制作、成都網站建設網站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元平陽做網站,已為上家服務,為平陽各地企業和個人服務,聯系電話:13518219792上一篇我們也說過了,泛型可以是類,結構,接口,在這些泛型類型中定義的方法都可以叫做泛型方法,都可以引用由泛型類型本身指定的一個類型參數例如:
public class GenericType<T>
{
private T G_Value;
public T Convert<T> { T res = (T)Convert.ChangeType(G_value,typeof(T)); return res; }
}
泛型方法的存在為我們提供了極大的靈活性。
泛型方法顧名思義,首先它是一個方法,只不過這個方法的返回值,參數可能是泛型的,
類型推斷
類型推斷就是根據泛型方法中定義的形參類型T,在我們調用的使用,那么所要傳的也應該是一致的。例如:
static void Test<T >(ref T t1,ref T t2)
{
T temp = t1;
t1 = t2;
t2 = temp;
}
這是我們定義的一個泛型方法,第一個參數類型為 T,第二個也為 T,說明這兩個類型是一致的。那么我們在調用的時候應該這樣 :
int i = 8, j = 0 ; Test( ref i, ref j);
而如果我們兩個實參的類型不統一的話,那么就會引發編譯異常例如 :
開放類型和封閉類型
具有泛型類型參數的類型對象讓就還是一個類型, CLR內部依舊和其他的對象類型一樣創建一個內部類型對象, 那么這種具有泛型類型參數的類型稱之為開放類型,開發類型不能進行構造任何的實例.
當泛型類型參數是一個實參時,那么所有的類型實參傳遞的都是實際的數據類型了,這種類型稱之為 封閉類型,封閉類型是允許構造實例對象的
class DictionaryStringKey <T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() { throw new NotImplementedException(); } System .Collections. IEnumerator System. Collections.IEnumerable .GetEnumerator() { throw new NotImplementedException(); } }
在上述代碼中,有一個DictionaryStringKey的類,繼承了一個IEnumable<T>的泛型,那么根據我們的開放類型它在調用的時候是不只是具體的實參,并且不能被構造實例的.
static void Main( string[] args) { object o = null; Type t = typeof( DictionaryStringKey <>); o = CreateInstance(t); Console .ReadLine(); } private static object CreateInstance( Type t) { object o = null; try { o = Activator . CreateInstance(t); Console .WriteLine( @" 創建了 " + t + "的實例 " , t .ToString()); } catch (Exception ex) { o = Activator . CreateInstance(t); Console .WriteLine( @" 創建 " + t + "的實例失敗 " , t .ToString()); } return o; }
上述代碼中我嘗試著對開放類型進行實例化,結果報出異常 :
當我們傳入實參之后,那么它就是一個封閉類型,這樣我們就可以進行構造實例了.
static void Main( string[] args) { object o = null; Type t = typeof( DictionaryStringKey <string > ); o = CreateInstance(t); Console .ReadLine(); }
泛型的主要作用就是定義泛型的引用類型和值類型,在CLR中,對于泛型接口的支持也是很重要的,因為這樣更有益與程序的擴展性, 另外一點如果沒有泛型接口我們每次使用非泛型接口時都會進行裝箱操作(例如: IComparable),并且會失去編譯時的安全性(因為它是非泛型的,較之泛型的好處之一).
那么CLR提供了對于泛型接口的支持,對于引用類型和值類型可以通過制定實參的方式來實現泛型接口,同時也可以保持未指定狀態來實現一個泛型接口( 接受者也可以是泛型 , 可以在后期進行指定類型)
實例 1 : 聲明一個泛型接口
interface IPair <T> { T First { get; set ; } T Second { get; set ; } }
IPair這個接口實現了一對相關的對象,兩個數據項具有相同的類型.
實現接口時,語法與非泛型類的語法是相同的,如果在實現泛型接口時,同時不指定實參,則默認為是一個泛型類.
實例 2 : 泛型接口的實現
class Pair <T> : IPiar< T> { public T First { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public T Second { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } }
CLR泛型接口的支持對于集合類型來講尤其重要,同時泛型在集合類型中較為常用,如果沒有泛型那么我們所使用List的時候都要依賴與System.Collections,我們在每次訪問的時候都需要執行一次轉換,如果使用泛型接口,就可以避免執行轉型,因為參數化的接口能實現更強的編譯時綁定.
實例 3 : 在一個類中多次實現相同的接口
interface IContainer <T> { ICollection<T > Items { get ; set; } } public class Person : IContainer<A >,IContainer <B> { public ICollection <A> Items { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } ICollection<B > IContainer <B>. Items { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } }
Items屬性使用一個顯式接口實現多次出現,每一次,類型參數都有所不同,如果沒有泛型,這是做不到的,在非泛型的情況下,CLR只允許一個顯式的IContainer.Items屬性.
約束也可以理解為是一種規則,意思就是我們所編寫的所有泛型,你在調用的時候,應該給我傳輸那些類型的實參。
在上一篇中,我們也在一個DataTableToList中使用到了約束,但是并沒有進行講解,在這里我們詳細的說明一下:
在.NET 中有4中約束方式,它們的常規語法是相同的,約束要放在泛型方法或者泛型類型聲明的末尾,并由上下文關鍵字 ”where :“ 來引入 。
主要約束(引用類型約束):
主要約束表示的是 : 我們在指定一個引用類型約束時,那么一個指定的類型實參要么是與約束類型相同的類型,要么是從約束類型派生的一個類型
實例 4 : 引用類型約束
struct Generic<T>(T t1) where : class
這里我定義了一個泛型方法,Generic,后來寫了一個where : class,這就告訴編譯器,在使用者調用這個方法時,必須為這個方法的傳入一個引用類型的實參,同時,我們也可以注意到這個方法的返回類型為struct值類型。
主要約束(值類型約束)
很明顯,值類型約束的話,就是將 約束條件指定為 Struct
實例 5 : 值類型約束
class ConstraintOfStruct <T> where T : struct { public T result() { return new T(); } }
在代碼中,我們最后返回的是new T(),這個是沒有問題的,因為我們已經可以肯定T就是值類型,所有的值類型都有一個隱式的構造函數,那么如果我們將 約束條件改為 class的話,那么就會出異常,因為不是所有的引用類型都可以實例化.
實例 6 : 反面教材 - 約束為引用類型時進行實例化.
構造函數類型約束
構造函數類型約束表示為 T : new(),這個約束必須為所有的類型參數的最后一個約束,它用于檢查類型實參是否有一個可用于創建類型實例的無參構造函數,這一點比較適用于所有的值類型,所有的沒有顯式聲明構造函數的非靜態、非抽象類,所有顯式聲明了一個公共無參構造函數的非抽象類。
實例 7 : 構造函數類型約束
public T CreateInatance<T >() where T : new()
{ return new T(); }
在方法createInstance中,要求我們必須傳入一個無參的構造函數,例如:我們可以傳入 object,struct,stream等,但是不能傳入 string,Directory等.
接口約束:
接口約束的一個好處就是我們可以指定多個接口,但是只能指定一個類
class ConstraintOfStruct <T> where T : class,IEquatable <T> ,IComparable<T>{}
組合約束 :
組合約束就是指定多個約束條件在上一個約束中我們已經看到了 ,更詳細的如下 :
class Sample<T> where T : class ,IDisposable,new(){} class Sample<T> where T : struct,IDisposable{} class Sample<T,U> where T : Stream where U : IDisposable{} class Sample<T> where T : class ,struct
public static T Desercialies <T > (Stream stream, IFormatter formatter) { return (T)formatter. Deserialize(stream); }
formatter 負責將流轉換為 Object,返回一個Object類型,我們在使用泛型調用的時候應該這樣來 :
string result = Desercialies <string > (stream, formatter);
上述的代碼看著是一種強制轉換,但是仍然執行了隱式的轉換,就和使用非泛型 string result = (string)Desercialies(stream, formatter); 一樣
屬性也可以應用到泛型類型中,使用的方式和非泛型類型相同.自定義的屬性只允許引用開發泛型類型
class MyClass<T,S> where T :class
where S : struct
{
public T MyName { get; set; }
public S MyCode { get; set; }
public List<T> MyCourse { get; set; }
public List<S> MyStudyHistory { get; set; }
}
上述我們定義了一個MyClass 類,這個類有兩個參數一個是引用類型一個是值類型.在屬性MyName,MyCourse的類型都是引用類型,MyCode的類型都為值類型
泛型類型構造器:
class Pair < T> : IPiar< T >
{
private T _first;
public T First
{
get { return _first; }
set { _first = value; }
}
private T _second;
public T Second
{
get { return _second; }
set { _second = value; }
}
public Pair(T first,T second)
{
}
}
interface IPiar < T> { }
型的構造器不需要添加類型參數來與類的聲明一致.
先到這里, 下一篇著重分享協變和逆變,否則這一篇符太長,不利于以后的查看.
泛型委托和泛型反射留在深入總結委托和反射的時候進行總結。
如果你覺得本文對你有幫助的話,請點右下角的推薦,或者直接關注我,后續將不斷更新.NET解析這一系列的文章....
另外有需要云服務器可以了解下創新互聯scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業上云的綜合解決方案,具有“安全穩定、簡單易用、服務可用性高、性價比高”等特點與優勢,專為企業上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
本文題目:.NET泛型解析(下)-創新互聯
當前網址:http://vcdvsql.cn/article30/hssso.html
成都網站建設公司_創新互聯,為您提供動態網站、網站改版、全網營銷推廣、ChatGPT、微信小程序、網站建設
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯