十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
上一篇對(duì).NET中的泛型進(jìn)行了詳細(xì)的介紹以及使用泛型的好處是什么,這篇將更加深入的去了解泛型的其他的知識(shí)點(diǎn),重頭戲.
創(chuàng)新互聯(lián)建站專注于企業(yè)營(yíng)銷型網(wǎng)站、網(wǎng)站重做改版、平山網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5場(chǎng)景定制、商城網(wǎng)站制作、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為平山等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
上一篇我們也說(shuō)過(guò)了,泛型可以是類,結(jié)構(gòu),接口,在這些泛型類型中定義的方法都可以叫做泛型方法,都可以引用由泛型類型本身指定的一個(gè)類型參數(shù)例如:
public class GenericType
{
private T G_Value;
public T Convert
}
泛型方法的存在為我們提供了極大的靈活性。
泛型方法顧名思義,首先它是一個(gè)方法,只不過(guò)這個(gè)方法的返回值,參數(shù)可能是泛型的,
類型推斷
類型推斷就是根據(jù)泛型方法中定義的形參類型T,在我們調(diào)用的使用,那么所要傳的也應(yīng)該是一致的。例如:
static void Test
{
T temp = t1;
t1 = t2;
t2 = temp;
}
這是我們定義的一個(gè)泛型方法,第一個(gè)參數(shù)類型為 T,第二個(gè)也為 T,說(shuō)明這兩個(gè)類型是一致的。那么我們?cè)谡{(diào)用的時(shí)候應(yīng)該這樣 :
int i = 8, j = 0 ; Test( ref i, ref j);
而如果我們兩個(gè)實(shí)參的類型不統(tǒng)一的話,那么就會(huì)引發(fā)編譯異常例如 :

開(kāi)放類型和封閉類型
具有泛型類型參數(shù)的類型對(duì)象讓就還是一個(gè)類型, CLR內(nèi)部依舊和其他的對(duì)象類型一樣創(chuàng)建一個(gè)內(nèi)部類型對(duì)象, 那么這種具有泛型類型參數(shù)的類型稱之為開(kāi)放類型,開(kāi)發(fā)類型不能進(jìn)行構(gòu)造任何的實(shí)例.
當(dāng)泛型類型參數(shù)是一個(gè)實(shí)參時(shí),那么所有的類型實(shí)參傳遞的都是實(shí)際的數(shù)據(jù)類型了,這種類型稱之為 封閉類型,封閉類型是允許構(gòu)造實(shí)例對(duì)象的
class DictionaryStringKey: IEnumerable { public IEnumerator GetEnumerator() { throw new NotImplementedException(); } System .Collections. IEnumerator System. Collections.IEnumerable .GetEnumerator() { throw new NotImplementedException(); } }
在上述代碼中,有一個(gè)DictionaryStringKey的類,繼承了一個(gè)IEnumable
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( @" 創(chuàng)建了 " + t + "的實(shí)例 " , t .ToString());
}
catch (Exception ex)
{
o = Activator . CreateInstance(t);
Console .WriteLine( @" 創(chuàng)建 " + t + "的實(shí)例失敗 " , t .ToString());
}
return o;
}上述代碼中我嘗試著對(duì)開(kāi)放類型進(jìn)行實(shí)例化,結(jié)果報(bào)出異常 :
當(dāng)我們傳入實(shí)參之后,那么它就是一個(gè)封閉類型,這樣我們就可以進(jìn)行構(gòu)造實(shí)例了.
static void Main( string[] args)
{
object o = null;
Type t = typeof( DictionaryStringKey );
o = CreateInstance(t);
Console .ReadLine();
} 泛型的主要作用就是定義泛型的引用類型和值類型,在CLR中,對(duì)于泛型接口的支持也是很重要的,因?yàn)檫@樣更有益與程序的擴(kuò)展性, 另外一點(diǎn)如果沒(méi)有泛型接口我們每次使用非泛型接口時(shí)都會(huì)進(jìn)行裝箱操作(例如: IComparable),并且會(huì)失去編譯時(shí)的安全性(因?yàn)樗欠欠盒偷?較之泛型的好處之一).
那么CLR提供了對(duì)于泛型接口的支持,對(duì)于引用類型和值類型可以通過(guò)制定實(shí)參的方式來(lái)實(shí)現(xiàn)泛型接口,同時(shí)也可以保持未指定狀態(tài)來(lái)實(shí)現(xiàn)一個(gè)泛型接口( 接受者也可以是泛型 , 可以在后期進(jìn)行指定類型)
實(shí)例 1 : 聲明一個(gè)泛型接口
interface IPair{ T First { get; set ; } T Second { get; set ; } }
IPair這個(gè)接口實(shí)現(xiàn)了一對(duì)相關(guān)的對(duì)象,兩個(gè)數(shù)據(jù)項(xiàng)具有相同的類型.
實(shí)現(xiàn)接口時(shí),語(yǔ)法與非泛型類的語(yǔ)法是相同的,如果在實(shí)現(xiàn)泛型接口時(shí),同時(shí)不指定實(shí)參,則默認(rèn)為是一個(gè)泛型類.
實(shí)例 2 : 泛型接口的實(shí)現(xiàn)
class Pair: 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泛型接口的支持對(duì)于集合類型來(lái)講尤其重要,同時(shí)泛型在集合類型中較為常用,如果沒(méi)有泛型那么我們所使用List的時(shí)候都要依賴與System.Collections,我們?cè)诿看卧L問(wèn)的時(shí)候都需要執(zhí)行一次轉(zhuǎn)換,如果使用泛型接口,就可以避免執(zhí)行轉(zhuǎn)型,因?yàn)閰?shù)化的接口能實(shí)現(xiàn)更強(qiáng)的編譯時(shí)綁定.
實(shí)例 3 : 在一個(gè)類中多次實(shí)現(xiàn)相同的接口
interface IContainer{ ICollection Items { get ; set; } } public class Person : IContainer,IContainer { public ICollection Items { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } ICollection IContainer . Items { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } }
Items屬性使用一個(gè)顯式接口實(shí)現(xiàn)多次出現(xiàn),每一次,類型參數(shù)都有所不同,如果沒(méi)有泛型,這是做不到的,在非泛型的情況下,CLR只允許一個(gè)顯式的IContainer.Items屬性.
約束也可以理解為是一種規(guī)則,意思就是我們所編寫的所有泛型,你在調(diào)用的時(shí)候,應(yīng)該給我傳輸那些類型的實(shí)參。
在上一篇中,我們也在一個(gè)DataTableToList中使用到了約束,但是并沒(méi)有進(jìn)行講解,在這里我們?cè)敿?xì)的說(shuō)明一下:
在.NET 中有4中約束方式,它們的常規(guī)語(yǔ)法是相同的,約束要放在泛型方法或者泛型類型聲明的末尾,并由上下文關(guān)鍵字 ”where :“ 來(lái)引入 。
主要約束(引用類型約束):
主要約束表示的是 : 我們?cè)谥付ㄒ粋€(gè)引用類型約束時(shí),那么一個(gè)指定的類型實(shí)參要么是與約束類型相同的類型,要么是從約束類型派生的一個(gè)類型
實(shí)例 4 : 引用類型約束
struct Generic(T t1) where : class
這里我定義了一個(gè)泛型方法,Generic,后來(lái)寫了一個(gè)where : class,這就告訴編譯器,在使用者調(diào)用這個(gè)方法時(shí),必須為這個(gè)方法的傳入一個(gè)引用類型的實(shí)參,同時(shí),我們也可以注意到這個(gè)方法的返回類型為struct值類型。
主要約束(值類型約束)
很明顯,值類型約束的話,就是將 約束條件指定為 Struct
實(shí)例 5 : 值類型約束
class ConstraintOfStructwhere T : struct { public T result() { return new T(); } }
在代碼中,我們最后返回的是new T(),這個(gè)是沒(méi)有問(wèn)題的,因?yàn)槲覀円呀?jīng)可以肯定T就是值類型,所有的值類型都有一個(gè)隱式的構(gòu)造函數(shù),那么如果我們將 約束條件改為 class的話,那么就會(huì)出異常,因?yàn)椴皇撬械囊妙愋投伎梢詫?shí)例化.
實(shí)例 6 : 反面教材 - 約束為引用類型時(shí)進(jìn)行實(shí)例化.

構(gòu)造函數(shù)類型約束
構(gòu)造函數(shù)類型約束表示為 T : new(),這個(gè)約束必須為所有的類型參數(shù)的最后一個(gè)約束,它用于檢查類型實(shí)參是否有一個(gè)可用于創(chuàng)建類型實(shí)例的無(wú)參構(gòu)造函數(shù),這一點(diǎn)比較適用于所有的值類型,所有的沒(méi)有顯式聲明構(gòu)造函數(shù)的非靜態(tài)、非抽象類,所有顯式聲明了一個(gè)公共無(wú)參構(gòu)造函數(shù)的非抽象類。
實(shí)例 7 : 構(gòu)造函數(shù)類型約束
public T CreateInatance() where T : new()
{
return new T();
}在方法createInstance中,要求我們必須傳入一個(gè)無(wú)參的構(gòu)造函數(shù),例如:我們可以傳入 object,struct,stream等,但是不能傳入 string,Directory等.
接口約束:
接口約束的一個(gè)好處就是我們可以指定多個(gè)接口,但是只能指定一個(gè)類
class ConstraintOfStructwhere T : class,IEquatable ,IComparable {}
組合約束 :
組合約束就是指定多個(gè)約束條件在上一個(gè)約束中我們已經(jīng)看到了 ,更詳細(xì)的如下 :
class Samplewhere T : class ,IDisposable,new(){} class Sample where T : struct,IDisposable{} class Sample where T : Stream where U : IDisposable{} class Sample where T : class ,struct
public static T Desercialies(Stream stream, IFormatter formatter) { return (T)formatter. Deserialize(stream); }
formatter 負(fù)責(zé)將流轉(zhuǎn)換為 Object,返回一個(gè)Object類型,我們?cè)谑褂梅盒驼{(diào)用的時(shí)候應(yīng)該這樣來(lái) :
string result = Desercialies(stream, formatter);
上述的代碼看著是一種強(qiáng)制轉(zhuǎn)換,但是仍然執(zhí)行了隱式的轉(zhuǎn)換,就和使用非泛型 string result = (string)Desercialies(stream, formatter); 一樣
屬性也可以應(yīng)用到泛型類型中,使用的方式和非泛型類型相同.自定義的屬性只允許引用開(kāi)發(fā)泛型類型
class MyClass
where S : struct
{
public T MyName { get; set; }
public S MyCode { get; set; }
public List
public List MyStudyHistory { get; set; }
}
上述我們定義了一個(gè)MyClass 類,這個(gè)類有兩個(gè)參數(shù)一個(gè)是引用類型一個(gè)是值類型.在屬性MyName,MyCourse的類型都是引用類型,MyCode的類型都為值類型
泛型類型構(gòu)造器:
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> { }
型的構(gòu)造器不需要添加類型參數(shù)來(lái)與類的聲明一致.
先到這里, 下一篇著重分享協(xié)變和逆變,否則這一篇符太長(zhǎng),不利于以后的查看.
泛型委托和泛型反射留在深入總結(jié)委托和反射的時(shí)候進(jìn)行總結(jié)。
如果你覺(jué)得本文對(duì)你有幫助的話,請(qǐng)點(diǎn)右下角的推薦,或者直接關(guān)注我,后續(xù)將不斷更新.NET解析這一系列的文章....