第十二章 泛型
目錄:
12.1 FCL中的泛型
12.2 泛型基礎結構
12.3 泛型接口
12.4 泛型委托
12.5 委托和接口的逆變和協變泛型類型實參
12.6 泛型方法
12.7 泛型和其他成員
12.8 可驗證性和約束
?
泛型時CLR和編程語言提供的一種特殊機制,它支持另一種形式的代碼重用,即“算法重用”。
大多數算法允許都封裝在一個類型中,CLR允許創建泛型引用類型和泛型值類型,但不允許創建泛型枚舉類型。CLR還允許創建泛型接口和泛型委托。方法偶爾也封裝有用的算法,所以CLR允許在引用類型,值類型或接口中定義泛型方法。
定義泛型類型或方法時,為類型指定的任何變量(比如T)都稱為類型參數。T是變量名,源代碼能使用數據類型的任何地方都能使用T。
使用泛型類型或方法時指定的具體數據類型稱為類型實參。
優勢:
源代碼保護:使用泛型算法的開發人員不需要訪問算法的源代碼。
類型安全:將泛型算法應用于一個具體的類型時,編譯器和CLR能理解開發人員的意圖,并保證只有與指定數據類型兼容的對象才能用于算法。
更清晰的代碼:由于編譯器強制類型安全性,所以減少了源代碼中必須進行的強制類型轉換次數,使代碼易于編寫和維護。
更佳的性能:CLR不需要執行裝箱操作,值類型的實例以傳值的方式傳遞。
12.1 FCL中的泛型
泛型最明顯的應用就是集合類。集合類型實現了許多接口,放入集合中的對象可實現接口來執行排序和搜索等操作。
12.2 泛型基礎結構
CLR添加泛型需要的工作:
創建新的IL指令,使之能夠識別類型參數。
修改現有元數據表的格式,以便表示具有泛型參數的類型名稱和方法。
修改各種編程語言(C#,Microsoft Visual Basic .NET等)來支持新語法,允許開發人員定義和引用泛型類型和方法。
修改編譯器,使之能生成新的IL指令和修改的元數據格式。
修改JIT編譯器,以便處理新的支持類型實參的IL指令來生成正確的本機代碼。
創建新的反射成員,使開發人員能查詢類型和成員,以判斷它們是否具有泛型參數。另外,還必須定義新的反射成員,使開發人員能在運行時創建泛型類型和方法定義。
修改調試器以顯示和操縱泛型類型,成員,字段以及局部變量。
修改VS的“智能感知”功能。將泛型類型或方法應用于特性數據類型時能顯示成員的原型。
12.2.1 開放類型和封閉類型
具有泛型類型參數的類型成為開發類型,CLR禁止構造開放類型的任何實例。這類似于CLR禁止構造接口類型的實例。
代碼引用泛型類型時可指定一組泛型類型實參。為所有類型參數都傳遞了實際的數據類型,類型就成為封閉類型。CLR允許構造封閉類型的實例。然而,代碼引用泛型類型的時候,可能留下一些泛型類型實參未指定。這會在CLR中創建新的開發類型對象,而且不能創建該類型的實例。
12.2.2 泛型類型和繼承
泛型類型仍然時類型,所以能從其他任何類型派生。使用泛型類型并指定類型實參時,實際是在CLR中定義一個新的類型對象,新的類型對象從泛型類型派生自的那個類型派生。
12.2.3 泛型類型同一性
12.2.4 代碼爆炸
使用泛型類型參數的方法在進行JIT編譯時,CLR獲取方法的IL,用指定的類型實參替換,然后創建恰當的本機代碼(這些代碼未操作指定數據類型“量身定制”)。缺點:CLR要為每種不同的方法/類型組合生成本機代碼。這個現象時“代碼爆炸”。
CLR內建了一些優化措施能緩解代碼爆炸。假如為特定的類型實參調用了一個方法,以后再用相同的類型實參調用這個方法,CLR只會為這個方法/類型組合編譯一次代碼。
CLR還有另一優化,它認為所有引用類型實參都完全相同,所以代碼能夠共享。(所有引用類型的參數或變量實際上只是指向堆上對象的指針,所有對象指針都以相同方式操縱)
如果某個類型時值類型,CLR就必須專門為那個值類型生成本機代碼。因為值類型的大小不定,CLR無法共享代碼,因為可能要用不同的本機CPU指令來操縱這些值。
12.3 泛型接口
使用非泛型接口來操縱值類型會發生裝箱,而且會失去編譯時的類型安全性。更到內容請參考在13章接口
12.4 泛型委托
CLR支持泛型委托,目的是保證任何類型的對象都能以類型安全的方式傳給回調函數。泛型委托允許值類型實例在傳給給回調方法時不進行任何裝箱。?
12.5 委托和接口的逆變和協變泛型類型實參
委托的每個泛型類型參數都可標記為協變量或逆變量。這樣就可將泛型委托類型的變量轉換為相同的委托類型(但泛型參數類型不同)。泛型類型參數:
不變量:意味著泛型類型參數不能更改。
逆變量:意味著泛型類型參數可以從一個類更改為它的某個派生類。在C#中用in關鍵字標記逆變量形式的泛型類型參數。逆變量泛型類型參數只出現在輸入位置,比如作為方法的參數。
協變量:意味著泛型類型參數可以從一個類更改為它的某個基類。C#是用out關鍵字標記協變量形式的泛型類型參數。協變量泛型類型參數只能出現在輸出位置,比如作為方法的返回類型。
(協變性指定返回類型的兼容性,逆變性指定參數的兼容性)
對于泛型類型參數,如果要將該類型的實參傳給使用out或ref關鍵的方法,便不允許可變性。
12.6 泛型方法
定義泛型類,結構或方法時,類型中定義的任何地方都可引用類型指定的類型參數。類型參數可作為方法參數,方法返回值或方法內部定義的局部變量的類型使用。CLR也允許方法指定它自己的類型參數。這些參數也可以作為參數,返回值,或局部變量使用。
泛型方法和類型推斷
C#編譯器支持在調用泛型方法時進行類型推斷。這意味著編譯器會在調用泛型方法時自動推斷要使用的類型。
12.7 泛型和其他成員
在C#中,屬性,索引器,事件,操作符方法,構造器和終結器本身不能有類型參數。但它們能在泛型類型中定義,而且這些成員中的代碼能使用類型的類型參數。
12.8 可驗證性和約束
?約束機制:約束的作用是限制能指定成泛型實參的類型數量。通過限制類型的數量,可以對那些類型執行更多操作。
約束可應用于泛型類型的類型參數,也可用于泛型方法的類型參數。CLR不允許基于類型參數名稱或約束來進行重載;只能基于元數(類型參數個數)對類型或方法進行重載。
重寫虛泛型方法時,重寫的方法必須指定相同數量的類型參數,而且這些類型參數繼承在基類方法上指定的約束。事實上,根本不允許為重寫方法的類型參數指定任何約束。但類型參數的名稱是可以改變的。類似地,實現接口方法時,方法必須指定與接口方法等量地類型參數,這些類型參數將繼承由接口方法指定地約束。
12.8.1 主要約束
類型參數可以指定零個或者一個主要約束。主要約束可以代表非密封類地一個引用類型。
指定引用類型約束時,相當于向編譯器承諾:一個指定的類型實參要么時與約束類型相同的類型,要么時從約束類型派生的類型。
12.8.2 次要約束
類型參數可以指定零個或者多個次要約束,次要約束代表接口類型。這種約束向編譯器承諾類型實參實現了接口。由于能指定多個接口約束,所以類型實參必須實現了所有接口約束。
還有一種次要約束稱為類型參數約束,有時也稱為裸類型約束。這種約束用得比接口約束少得多。它允許一個泛型類型或方法規定:指定的類型實參要么就是約束的類型,要么時約束的類型的派生類。一個類型參數可以指定零個或者多個類型參數約束。
12.8.3 構造器約束
類型參數可指定零個或一個構造器約束,它向編譯器承諾類型實參是是實現了公共無參構造器的非抽象類型。where T : new()
12.8.4 其他可驗證性問題
1.泛型類型變量的轉型
將泛型類型的變量轉型為其他類型是非法的,除非轉型為與約束兼容的類型。
2.將泛型類型變量設為默認值
將泛型類型變量設為null是非法的,除非將泛型類型約束成引用類型。
3.將泛型類型變量與null進行比較
無論泛型類型是否被約束,使用==或!=操作符將泛型類型變量與null進行比較都是合法的。
4.兩個泛型類型變量相互比較
如果泛型類型參數不能肯定是引用類型,對同一個泛型類型的兩個變量進行比較是非法的。
5.泛型類型變量作為操作數使用
將操作符應用于泛型類型操作數會出現大量問題。不能將操作符應用于泛型類型的變量。
?
轉載于:https://www.cnblogs.com/terry-1/p/10028569.html
總結
- 上一篇: 纸条app怎么复制(纸条作文app下载最
- 下一篇: 利用cookie模拟登陆知乎