程序員常用的泛型機制究竟怎麼玩?

程序員常用的泛型機制究竟怎麼玩?

頭圖 | CSDN 下載自東方IC

出品 | CSDN(ID:CSDNnews)

開發人員會經常用到泛型,並且大部分開發人員都會很好的使用泛型。泛型需要底層運行時的支持,泛型類中類型參數變成了元數據,在運行時需要時會利用她們來構造恰當的類,因此泛型支持繼承、多態和封裝。簡單地說了一下泛型深層的知識,下面我們來看看泛型的內部機制到底是什麼。

程序员常用的泛型机制究竟怎么玩?

CIL 表示泛型

泛型類在經過編譯後其實和普通類編譯後的結果並沒什麼太大的出入,結果都是 CIL 和元數據,唯一的區別是 CIL 會用特殊的標記來表示該編譯結果為泛型類的編譯結果,我們通過一個例子來看一下。

<code>public class Demo where T:IComparable/<code>
<code>{/<code>
<code> //more code/<code>
<code> T item;/<code>
<code> //more code/<code>
<code>}/<code>

上述代碼中定義了一個 Demo 泛型類,它包含一個類型參數。經過變異後最終生成的參數化 CIL 代碼如下:

<code>.class private auto ansi beforefieldinit Demo'1  extends [mscorlib]System.Object/<code>
<code>{/<code>
<code> //more CIL code/<code>
<code> .field private !0 items/<code>
<code> //more CIL code/<code>
<code>}/<code>

在上述參數化 CIL 代碼中我們看到在 Demo 後面出現了一個 '1 ,它叫做元數也就是參數數量,表示聲明泛型類時要求的類型實參的數量,上面的這個例子因為只一個類型參數因此他需要的類型實參的數量就是 1 個,如果是 Demo<tkey> 這樣的泛型類的話,生成的參數化 CIL 代碼中的類型實參數量就是 3,表示為 '3 。在參數化 CIL 代碼中顯示了施加在類上的約束,既 [mscorlib]System.IComparable 。同時 CIL 代碼修改了定義的 T 類型數組聲明,使用 ! 包含一個類型參數。除了上說所說的三點之外,生成的其他 CIL 代碼和普通類生成的代碼沒什麼區別。/<tkey>

程序员常用的泛型机制究竟怎么玩?

值類型泛型實例化

當用值類型作為類型參數構造泛型類型時為了創建具體化的泛型類型,運行時會在 CIL 合適的位置放上指定的類型參數。

當我們首次調用上一小節中定義的泛型類 Demo 並將 int 作為類型實參傳遞給 Demo 時,運行時會生成 Demo 的具體化版本,並使用 int 替換類型參數。這樣每次使用 Demo 的時候運行時都會重用已經生成的具體化類 Demo 。這裡需要注意的是生成的具體化類只針對類型實參是 int 的情況下,如果這時又定義了一個以 float 為類型實參的 Demo 時,運行時將會再生成泛型類型的另一個版本。

使用值類型泛型實例能避免代碼的轉換和裝箱操作進而提高性能,但是在使用過程中也需要根據具體代碼、具體情況和具體項目來判斷值類型泛型是否該使用該怎麼使用。

Tip:運行時會為每個新的只類型參數創建一個具體的泛型類型。

程序员常用的泛型机制究竟怎么玩?

引用類型泛型實例化

泛型實例在使用引用類型作為類型實參的時候和值類型有點不同。在構造泛型類型時運行時會在 CIL 代碼中使用 object 引用替換類型參數來創建具體化泛型類型,當每次使用引用類型參數實例化構造好的類型時,運行時都會重用已經生成好的版本,這裡需要注意的是如果提供的引用類型和構造泛型類型時的引用類型不同,它依然會使用已經生成好的那個版本。下面看一個例子:

<code>Demo<user> userDemo;/<user>/<code>
<code>Demo<student> studentDemo;/<student>/<code>

首先當代碼運行到第一行時,運行時會生成 Demo 的具體化版本,CIL 不會存儲 User 作為制定的數據類型,而是存儲 object 引用。然後代碼運行到第二行時,雖然 Student 和 User 的引用不同但是 CIL 不會為 Student 創建一個新的具體化版本,而是實例化前面給予 object 引用的 Demo 的實例。當然,為了確保類型安全, CIL 會分配給 Order 類型一個內存區域來替換類型參數的每個 object 引用導致向這個內存區域。使用引用類型泛型的好處是編譯器將它創建的具體化類壓縮至 1 個,因此大大減少了代碼量,提高了代碼的性能。

這裡需要注意,引用類型類型參數在發生變化時運行時使用相同內部泛型類型定義,但是如果出現如下的情況,那麼就不會適用相同的內部泛型類型定義。

<code>Demo userDemo;/<code>
<code>Demo<long> userLongDemo;/<long>/<code>
<code>Demo<guid> userGuidDemo;/<guid>/<code>

在上述代碼中類型參數包含值類型,這時就不會使用相同的內部泛型類型定義,而是為每個 Demo 創建不同的內部類型定義。

程序员常用的泛型机制究竟怎么玩?

小結

這篇文章雖然簡短,但是充分講解了泛型機制的內部原理以及相關注意事項。.NET 中的泛型可以說是最好的,在 Java 中使用泛型完全是在編譯器中實現的而不是在 JVM 中實現的,雖然這樣可以預防使用泛型而分發新的 JVM ,但是由於它不區分值類型和引用類型,所以執行效率底以及影響到反射部分功能的使用。

作者:朱鋼,.NET高級開發工程師,7年一線開發經驗,參與過電子政務系統和AI客服系統的開發,以及互聯網招聘網站的架構設計,目前就職於北京恆創融慧科技發展有限公司。

程序员常用的泛型机制究竟怎么玩?

☞百年 IBM 終於 All In 人工智能和混合雲!

☞微軟、蘋果、谷歌、三星……這些區塊鏈中的科技巨頭原來已經做了這麼多事!

☞斬獲GitHub 2000+ Star,阿里雲開源的 Alink 機器學習平臺如何跑贏雙11數據“博弈”?| AI 技術生態論

☞微軟為一人收購一公司?破解索尼程序、寫黑客小說,看他彪悍的程序人生!

☞機器學習項目模板:ML項目的6個基本步驟

☞IBM、微軟、蘋果、谷歌、三星……這些區塊鏈中的科技巨頭原來已經做了這麼多事!

☞資深程序員總結:分析Linux進程的6個方法,我全都告訴你

今日福利:評論區留言入選,可獲得價值299元的「2020 AI開發者萬人大會」在線直播門票一張。 快來動動手指,寫下你想說的話吧。


分享到:


相關文章: