国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

[CLR via C#]12. 泛型

2019-11-17 03:20:55
字體:
供稿:網(wǎng)友

[CLR via C#]12. 泛型

  泛型(generic)是CLR和編程語言提供一種特殊機(jī)制,它支持另一種形式的代碼重用,即"算法重用"。

  簡單地說,開發(fā)人員先定義好一個(gè)算法,比如排序、搜索、交換等。但是定義算法的開發(fā)人員并不設(shè)定該算法要操作什么數(shù)據(jù)類型;該算法可廣泛地應(yīng)用于不同類型的對(duì)象。然后,另一個(gè)開發(fā)人員只要指定了算法要操作的具體數(shù)據(jù)類型,就可以使用這個(gè)現(xiàn)成的算法了。

  泛型有兩種表現(xiàn)形式:泛型類型泛型方法

  泛型類型:大多數(shù)算法都封裝在一個(gè)類型中,CLR允許創(chuàng)建泛型引用類型和泛型值類型,但不允許創(chuàng)建泛型枚舉類型。除此之外,CLR還允許創(chuàng)建泛型接口和泛型委托。

  泛型方法:方法偶爾也封裝有用的算法,所以CLR允許引用類型、值類型或接口中定義泛型方法。

  兩者都是表示API的基本方法(不管是指一個(gè)泛型方法還是一個(gè)完整的泛型類型),以致平時(shí)期望出現(xiàn)一個(gè)普通類型的地方出現(xiàn)一個(gè)類型參數(shù)。比如,List<T>,在類名之后添加一個(gè)<T>,表明它操作的是一個(gè)未指定的數(shù)據(jù)類型。定義泛型類型和方法時(shí),它為類型指定的任何變量(比如 T)都稱為類型參數(shù)(type parameter)。T代表一個(gè)變量名,在源代碼中能夠使用一個(gè)數(shù)據(jù)類型的任何位置 ,都能使用T。

  類型參數(shù)是真實(shí)類型的占位符。在泛型聲明中,類型參數(shù)要放在一堆尖括號(hào)內(nèi),并以逗號(hào)分隔。所以,在Dictionary<TKey, TValue>中,類型參數(shù)是TKey和TValue。使用泛型類型或方法時(shí),要使用真實(shí)的類型代替。這些真實(shí)的類型稱為類型實(shí)參(type argument)。

  泛型為開發(fā)人員提供了以下優(yōu)勢:

  1)源代碼保護(hù)  使用一個(gè)泛型算法的開發(fā)人員不需要訪問算法的源代碼。然而,使用C++模板的泛型技術(shù)時(shí),算法的源代碼必須提供給準(zhǔn)備使用算法的用戶。

  2)類型安全  將一個(gè)泛型算法應(yīng)用于一個(gè)具體的類型時(shí),編譯器和CLR能理解開發(fā)人員的意圖,并保證只有與制定數(shù)據(jù)類型兼容的對(duì)象才能隨同算法使用。

  3)更清晰的代碼  由于編譯器強(qiáng)制類型安全性,所以減少了源代碼中必須進(jìn)行的轉(zhuǎn)型次數(shù)。

  4)更佳的性能  在有泛型之前,要想定義一個(gè)常規(guī)化的算法,它的所有成員都要定義成操作Object數(shù)據(jù)類型。這其中就要有裝箱和拆箱之間的性能損失。由于現(xiàn)在能創(chuàng)建一個(gè)泛型算法來操作一個(gè)具體的值類型,所以值類型的實(shí)例能以傳值的方式傳遞,CLR不再需要只需任何裝箱操作。由于不再需要轉(zhuǎn)型,所以CLR不必檢查嘗試一次轉(zhuǎn)型操作是否類型安全,同樣提高了代碼的允許速度。

一、 Framework類庫中的泛型

  泛型最明顯的應(yīng)用就是集合類。FCL已經(jīng)定義了幾個(gè)泛型集合類。其中大多數(shù)類能在Sysytem.Collections.Generic和System.Collections.ObjectModel命名空間中。要使用線程安全的泛型集合類,可以去System.Collections.Concurrent命名空間尋找。  Microsoft建議開發(fā)人員使用泛型集合類,并基于幾個(gè)方面的原因,不鼓勵(lì)使用非泛型集合類。首先,非泛型無法獲得類型安全性、更清晰的代碼和更佳的性能。其次,泛型具有更好的對(duì)象模型。  集合類實(shí)現(xiàn)了許多接口,放入集合中的對(duì)象也可能實(shí)現(xiàn)了接口,集合類可利用這些接口執(zhí)行像排序這樣的操作。FCL內(nèi)建了許多泛型接口定義,所以在使用接口時(shí),也能體會(huì)到泛型帶來的好處。常用的接口包含在Sysytem.Collections.Generic命名空間中。    新的泛型接口并不是設(shè)計(jì)用來完全取代非泛型接口。  System.Array類(即所有數(shù)組的基類)提供了大量靜態(tài)泛型方法,比如,AsReadonly、FindAll、Find、FindIndex等。

二、Wintellect的Power Collections庫  Power Collections庫由Wintellect制作,這個(gè)庫有一系列集合類構(gòu)成,任何人都可以免費(fèi)下載和使用。  

集合類名稱說明
BigList<T>有序T對(duì)象集合。操作100個(gè)以上的數(shù)據(jù)項(xiàng)是,效率非常高
Bag<T>無序T對(duì)象的集合,集合進(jìn)行了哈希處理,并允許重復(fù)項(xiàng)
OrderedBag<T>有序T對(duì)象的集合,允許重復(fù)值
Set<T>無序T數(shù)據(jù)項(xiàng)集合,不允許重復(fù)項(xiàng)。添加重復(fù)項(xiàng)后,會(huì)只保留一個(gè)
OrderedSet<T>有序T數(shù)據(jù)項(xiàng)的集合,不允許重復(fù)項(xiàng)
Deque<T>雙端隊(duì)列(double-ending queue)。類似于一個(gè)列表,但在起始處添加/刪除數(shù)據(jù)項(xiàng)時(shí),比列表更高效
OrderedDictionary<TKey,TValue>字典,其中的鍵進(jìn)行了排序,每個(gè)鍵都有一個(gè)對(duì)應(yīng)的值
MultiDictionary<TKey,TValue>字典,其中每個(gè)鍵都可以有多個(gè)值,對(duì)鍵進(jìn)行了哈希處理,允許重復(fù),而且數(shù)據(jù)項(xiàng)是無序的
OrderedMultiDictionary<TKey,TValue>

字典,其中的鍵進(jìn)行了排序,每個(gè)鍵都可以有多個(gè)值(同樣進(jìn)行了排序)。允許重復(fù)的鍵

三、泛型的基礎(chǔ)結(jié)構(gòu)

  為了是泛型能夠工作,Microsoft必須完成以下工作:

1)創(chuàng)建新的IL指令,使之能夠識(shí)別類型實(shí)參 2)修改現(xiàn)有元數(shù)據(jù)表的格式,以便表示具有泛型參數(shù)的類型名稱和方法 3)修改各種編程語言(C#等),以支持新的語法,允許開發(fā)人員定義個(gè)引入泛型類型和方法 4)修改編譯器,使之能生成新的IL指令和修改元數(shù)據(jù)格式 5)修改JIT編譯器,使之能夠處理新的、支持類型實(shí)參的IL指令,以便生成正確的本地代碼 6)創(chuàng)建新的反射成員,使開發(fā)人員能查詢類型和成員,以判斷它們是否具有泛型參數(shù)。另外,還必須定義新的反射成員,使開發(fā)人員能在運(yùn)行時(shí)創(chuàng)建泛型類型和方法定義。 7)修改調(diào)試器以以顯示和操作泛型類型、成員、字段以及局部變量。 8)修改VisualStudio 的"智能感知"(IntelliSense)特性。 1.開放類型封閉類型  前面我們討論過CLR如何為應(yīng)用程序的每個(gè)類型創(chuàng)建一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)稱為類型對(duì)象。  具有泛型類型參數(shù)的類型仍然是類型,CLR同樣會(huì)為它創(chuàng)建一個(gè)內(nèi)部類型對(duì)象。無論是引用類型(類)、值類型(結(jié)構(gòu))、接口類型,還是委托類型,這一點(diǎn)都是成立的。  如果沒有為任何類型參數(shù)提供類型實(shí)參,聲明的就是一個(gè)未綁定泛型類型。  如果指定了類型實(shí)參,該類型就稱為已構(gòu)造類型。  我們知道,類型可以看做是對(duì)象的藍(lán)圖。同樣的,未綁定泛型類型是已構(gòu)造類型的藍(lán)圖。它是一種額外的抽象層。  已構(gòu)造類型可以是開放類型封閉類型。  "開放類型"(open type)是指還包含一個(gè)類型參數(shù),CLR禁止構(gòu)造開放類型的任何實(shí)例。這一點(diǎn)類似于CLR禁止構(gòu)造接口類型的實(shí)例。  代碼引用一個(gè)泛型類型時(shí),可指定一組泛型類型實(shí)參。假如為所有類型實(shí)參傳遞的都是實(shí)際數(shù)據(jù)類型,類型就稱為"封閉類型"(closed type)。也就是說,具有泛型"類型實(shí)參"的類型稱為"封閉類型"。CLR允許構(gòu)造封閉類型的實(shí)例。  當(dāng)代碼引用一個(gè)泛型類型時(shí),可能會(huì)留下一些泛型類型實(shí)參未指定。這會(huì)在CLR中創(chuàng)建一個(gè)新的開放類型的對(duì)象,而且不能創(chuàng)建該類型的實(shí)例。比如:
internal static class PRogram    {        private static void Main(string[] args)        {            Object o = null;             // Dictionary<,> 是一個(gè)開放類型,有兩個(gè)類型參數(shù)            Type t = typeof(Dictionary<,>);             // 嘗試創(chuàng)建該類型的一個(gè)實(shí)例 (失敗)            o = CreateInstance(t);            Console.WriteLine();             // DictionaryStringKey<> 是一個(gè)開放類型,有一個(gè)類型參數(shù)            t = typeof(DictionaryStringKey<>);             // 嘗試創(chuàng)建該類型的一個(gè)實(shí)例 (失敗)            o = CreateInstance(t);            Console.WriteLine();             // DictionaryStringKey<Guid> 是一個(gè)封閉類型            t = typeof(DictionaryStringKey<Guid>);             // 嘗試創(chuàng)建該類型的一個(gè)實(shí)例 (成功)            o = CreateInstance(t);             // Prove it actually worked            Console.WriteLine("Object type=" + o.GetType());             Console.ReadKey();        }         private static Object CreateInstance(Type t)        {            Object o = null;            try            {                o = Activator.CreateInstance(t);                Console.Write("已創(chuàng)建 {0} 的實(shí)例", t.ToString());            }            catch (ArgumentException e)            {                Console.WriteLine(e.Message);            }            return o;        }         // A partially specified open type        internal sealed class DictionaryStringKey<TValue> :            Dictionary<String, TValue>        {        }    }

  最后顯示地結(jié)果為:

  可以看出,Activator的CreateInstance方法會(huì)在構(gòu)造開發(fā)類型的實(shí)例時(shí)拋出一個(gè)ArgumentException異常。注意,在異常的字符串消息中,指明類型中仍然含有一些泛型參數(shù)。  從輸出結(jié)果可以看出,類型名是以一個(gè)"`"字符和一個(gè)數(shù)字結(jié)尾的。這個(gè)數(shù)字代表類型的元數(shù),也就是類型要求的類型參數(shù)的個(gè)數(shù)。例如,Dictionary類的元數(shù)為2,它要求為TKey和TValue這兩個(gè)類型參數(shù)指定具體類型。  還要注意的是,CLR會(huì)在類型對(duì)象內(nèi)部分配類型的靜態(tài)字段。因此,每個(gè)封閉類型都有自己的靜態(tài)字段。換言之,假如List<T>定義了任何靜態(tài)字段,這些字段不會(huì)在一個(gè)List<DataTime>和List<String>之間共享;每個(gè)封閉類型對(duì)象都有它自己的靜態(tài)字段。另外,假如一個(gè)泛型類型定義了一個(gè)靜態(tài)構(gòu)造器,那么針對(duì)每個(gè)封閉類型,這個(gè)構(gòu)造器都會(huì)執(zhí)行一次。在泛型類型上定義一個(gè)靜態(tài)構(gòu)造器的目的是保證傳遞的類型參數(shù)滿足特定的條件。例如,如果希望一個(gè)泛型類型值用于處理枚舉類型,可以如下定義:
internal sealed calss GenericTypeThatReqiresAnEnum<T> {    static GenericTypeThatReqiresAnEnum() {        if ( !typeof (T).IsEnum) {            throw new ArgumentException("T must be an enumerated type")        }    }}

  CLR提供了一個(gè)名為"約束"(constraint)的功能,可利用它更好地定義一個(gè)泛型類型來指出哪個(gè)類型實(shí)參是有效的。

 2.泛型類型和繼承  泛型類型仍然是類型,所以它能從其他任何類型派生。使用一個(gè)泛型類型并指定類型實(shí)參時(shí),實(shí)際上是在CLR中定義一個(gè)新的類型對(duì)象,新的類型對(duì)象是從派生該泛型類型的那個(gè)類型派生的。也就是說,由于List<T>是從Object派生的,那么List<String>和List<Guid>也是從Object派生的。 3. 泛型類型同一性  有的時(shí)候,泛型語法會(huì)將開發(fā)人員搞糊涂,所以有的開發(fā)人員定義了一個(gè)新的非泛型類類型,它從一個(gè)泛型類型派生,并指定了所有的類型實(shí)參。例如,為了簡化一下代碼:
List<DateTime> dt = new List<DateTime>();

一些開發(fā)人員可能首先定義下面這樣的一個(gè)類:

internal sealed class DateTimeList : List<DataTime> {        //這里無需放任何代碼!}

然后就可以進(jìn)一步簡化創(chuàng)建:

DateTimeList  dt = new DateTimeList ();

  這樣做表面上是方便了,但是決定不要單純處于增強(qiáng)源代碼的易讀性類這樣定義一個(gè)新類。這樣會(huì)喪失類型同一性(identity)和相等性(equivalence)。如下:

Boolean sameType = (typeof(List<DateTime>) == (typeof(DateTimeList));

  上述代碼運(yùn)行時(shí),sameType會(huì)初始化為false,因?yàn)楸容^的是兩個(gè)不同類型的對(duì)象。也就是說,假如一個(gè)方法的原型接受一個(gè)DateTimeList,那么不能將一個(gè)List<DateTime>傳給它。然而,如果方法的原型接受一個(gè)List<DateTime>,那么可以將一個(gè)DateTimeList傳給它,因?yàn)镈ateTimeList是從List<DateTime>派生的。

  C#提供一種方式,允許使用簡化的語法來引用一個(gè)泛型封閉類型,同時(shí)不會(huì)影響類的相等性——使用using指令。比如:
using DateTimeList = System.Collections.Generic.List<System.DateTime>;

  現(xiàn)在只想下面這行代碼時(shí),sameType會(huì)初始化為true:

Boolean sameType = (type(List<DateTime>) == (ypeof(DateTimeList));

  還有,可以使用C#的隱式類型局部變量功能,讓編譯器根據(jù)表達(dá)式的類型來推斷一個(gè)方法的局部變量的類型。

 4.代碼爆炸  使用泛型類型參數(shù)的一個(gè)方法在進(jìn)行JIT編譯時(shí),CLR獲取方法的IL,用指定的類型實(shí)參進(jìn)行替換,然后創(chuàng)建恰當(dāng)?shù)谋镜卮a。然而,這樣做有一個(gè)缺點(diǎn):CLR要為每種不同的方法/類型組合生成本地代碼。我們將這個(gè)現(xiàn)象稱為"代碼爆炸"。它可能造成引用程序集的顯著增大,從而影響性能。  CLR內(nèi)建了一些優(yōu)化措施,能緩解代碼爆炸。首先,假如為一個(gè)特定的類型實(shí)參調(diào)用了一個(gè)方法,以后再次使用相同的類型實(shí)參來調(diào)用這個(gè)方法,CLR只會(huì)為這個(gè)方法/類型組合編譯一次。所以,如果一個(gè)程序集使用List<DateTime>,一個(gè)完全不同的程序集也使用List<DateTime>,CLR只會(huì)為List<DateTime>編譯一次方法。  CLR還提供了一個(gè)優(yōu)化措施,它認(rèn)為所有引用類型實(shí)參都是完全相同的,所以代碼能夠共享。之所以能這樣,是因?yàn)樗幸妙愋偷膶?shí)參或變量時(shí)間只是執(zhí)行堆上的對(duì)象的指針,而對(duì)象指針全部是以相同的方式操作的。  但是,假如某個(gè)類型實(shí)參是值類型,CLR就必須專門為那個(gè)值類型生
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 东莞市| 惠来县| 尖扎县| 武强县| 秀山| 安徽省| 泗洪县| 青龙| 松阳县| 长顺县| 龙陵县| 黎平县| 霍林郭勒市| 绵阳市| 晋中市| 陈巴尔虎旗| 博湖县| 永和县| 凤城市| 怀安县| 冀州市| 昭觉县| 陆河县| 无锡市| 肥乡县| 香格里拉县| 武山县| 都匀市| 友谊县| 宝坻区| 新乡市| 北京市| 桃江县| 涿鹿县| 河东区| 赫章县| 祁阳县| 林口县| 轮台县| 五原县| 犍为县|