詳解C#泛型(三)

Minotauros發表於2018-11-23

  一、前面兩篇文章分別介紹了定義泛型型別、泛型委託、泛型介面以及宣告泛型方法:

  詳解C#泛型(一)

  詳解C#泛型(二)

  首先回顧下如何構建泛型類:

public class MyClass<T>
{
    public void MyFunc()
    {
        //
    }
}

  其中,尖括號<>中的T代表的是該泛型類的型別引數,在使用時可以指定其型別,例如,指定型別引數為整數型別,建立封閉式構造類MyClass<int>:

MyClass<int> myObj = new MyClass<int>();

  二、這一篇我們瞭解一下泛型的作用機制,泛型是執行時起作用的一套機制,根據執行時型別引數被指定為值型別還是引用型別其使用方式有所不同:

  1.當型別引數被指定為值型別時,會在第一次指定該特定值型別的型別時建立該型別唯一的專用化泛型型別,泛型型別中的型別引數會被替換為相應的值型別;

  ※此時,執行時會建立不同封閉式構造型別的型別資訊物件,它們的型別控制程式碼指向不同的型別資訊物件,不同封閉式構造型別的方法控制程式碼也指向不同的方法資訊物件;

  2.當型別引數被指定為引用型別時,會在第一次指定任意引用型別時建立一個通用化泛型型別,泛型型別中的型別引數會被替換為該引用型別,並在之後每次指定為引用型別時重用該泛型型別並修改其中型別引數的型別;造成這種差異的原因可能在於所有的引用大小相同;

  ※此時,執行時依然會建立不同封閉式構造型別的型別資訊物件,它們的型別控制程式碼也指向不同的型別資訊物件,但是它們共用一套方法控制程式碼,即不同封閉式構造型別的方法控制程式碼指向同一個方法資訊物件;
  3.對於給定的泛型類:

public class MyClass<T>
{
    public void MyFunc()
    {
        //…
    }
}
獲取不同封閉式構造型別的型別控制程式碼和方法控制程式碼:
Type type1 = typeof(MyClass<int>);
Type type2 = typeof(MyClass<long>);
Type type3 = typeof(MyClass<string>);
Type type4 = typeof(MyClass<Array>);
//以下型別控制程式碼各不相同
Console.WriteLine(type1.TypeHandle.Value);
Console.WriteLine(type2.TypeHandle.Value);
Console.WriteLine(type3.TypeHandle.Value);
Console.WriteLine(type4.TypeHandle.Value);
//最後兩個方法控制程式碼相同,其它方法控制程式碼各不相同
Console.WriteLine(type1.GetMethod("MyFunc").MethodHandle.Value);
Console.WriteLine(type2.GetMethod("MyFunc").MethodHandle.Value);
Console.WriteLine(type3.GetMethod("MyFunc").MethodHandle.Value);
Console.WriteLine(type4.GetMethod("MyFunc").MethodHandle.Value);

  ※在訪問任何泛型型別之前,CLR會先建立MyClass<>的型別資訊物件;

  列印結果:

  

  可以發現,最後兩個泛型型別的MyFunc方法的方法控制程式碼指向相同;但是不同型別引數的情況下,還是會建立對應的泛型型別物件,這使得泛型單例成為可能:

  三、對於封閉式構造型別,只要其型別引數不完全相同,CLR就會在初次訪問該型別之前建立該型別的型別資訊物件並呼叫其對應唯一的靜態建構函式,例如對於有靜態建構函式的泛型類MyClass<T>,在初次訪問MyClass<int>、MyClass<string>等封閉式構造類之前都會呼叫一次其對應唯一的靜態建構函式,這也是建立泛型單例的基礎:

public class MyClass<T>
{
    static MyClass()
    {
        Console.WriteLine(typeof(T).FullName);
    }
}
MyClass<int> obj1 = new MyClass<int>();
MyClass<long> obj2 = new MyClass<long>();
MyClass<Array> obj4 = new MyClass<Array>();  

  列印結果:

  

  四、執行時動態構建泛型:

Type myType = typeof(MyClass<>);  //獲取未指定任何型別引數的開放式構造類的型別資訊,多個型別引數時新增,:typeof(MyClass<,>)
myType = myType.MakeGenericType(typeof(int));  //通過型別資訊的例項方法MakeGenericType()構建指定所有型別引數的封閉式構造類的型別資訊,如未指定所有型別引數會丟擲異常ArgumentException
//也可以直接獲取封閉式構造類的型別資訊,當型別引數在一開始就確定時推薦使用此種方式
//myType = typeof(MyClass<int>);  //多個型別引數時需要同時指定:typeof(MyClass<int, string>)

  注意:通過反射只可以獲取未指定任何型別引數的開放式構造類MyClass<,>的型別資訊和指定所有型別引數的封閉式構造類MyClass<int, string>的型別資訊,即無法獲取MyClass<int, >的型別資訊;

 


如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!

作者:Minotauros
出處:https://www.cnblogs.com/minotauros/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。

相關文章