程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 簡單分析.net泛型中的類型參數

簡單分析.net泛型中的類型參數

編輯:關於.NET

一位朋友詢問tppeof、GetType()、is、as的問題,在實驗的時候順手就用泛型寫的例子。在看Jit後的反匯編時發現了一個問題,clr對泛型參數有些特殊處理。上網查了一下沒有找到介紹泛型參數存儲的文章因此動手做了一番實驗,有了些淺顯的理解在此記錄下來望看到此文的高手能給予全面的解釋。

以前用泛型的時候沒想過clr是如何處理泛型參數的,今天查閱了下<<Expert .NET 2.0 IL Assembler>>裡面講解了一個叫做GenericParam Metadata Table的數據結構,不過沒能解決我的疑惑。先來看段代碼:

class C<T, U, V> where T: class
   {
     public void Test_Class(V v)
     {
       Debugger.Break();
       T t = v as T;
     }
     public void Test_Method<X, Y>(X x, Y y)
     {
       Debugger.Break();
       Type type1 = typeof(X);
       Type type2 = typeof(Y);
     }
   }
class Test
{
static void Main()
{
C<Type, string, object> c = new C<Type, string, object>();
       string s = "ok";
       object o = s;
       c.Test_Class(o);
       c.Test_Method(s, o);
}
}

我們先執行Test_Class方法,當進入Test_Class方法後部分主要反匯編及注釋如下:

T t = v as T;
0000003e mov     eax,dword ptr [ebp-3Ch] //1.得到this指針                       01E52B30
00000041 mov     eax,dword ptr [eax]      //2.得到C<T, U, V>的方法表地址        002A3918
00000043 mov     eax,dword ptr [eax+20h] //3.得到存儲泛型參數的地址            002A3954
00000046 mov     eax,dword ptr [eax]     //4.保存參數類型的地址                002a3958
00000048 mov     eax,dword ptr [eax]     //5.得到T參數的真實類型,既方法表地址 6C18172C
0000004a mov     dword ptr [ebp-48h],eax
0000004d test    dword ptr [ebp-48h],1
00000054 jne     0000005B
00000056 mov     ecx,dword ptr [ebp-48h] //將T的方法表地址傳給ECX准備call as方法
00000059 jmp     00000061
0000005b mov     eax,dword ptr [ebp-48h]  
0000005e mov     ecx,dword ptr [eax-1]
00000061 mov     edx,dword ptr [ebp-40h] //得到參數o
00000064 call    6C0598F3              //調用as方法

匯編的前兩句很好理解就是得到this指針後根據托管對象頭4字節找方法表地址,第三句是在方法表偏移20h出得到類型泛型參數信息,不過這裡要說明一點在msdn上有篇文章《深入探索.NET框架內部了解CLR如何創建運行時對象》裡面的有張ethodTable Layout的圖解,不過那個圖好像是.net1.1的和.net2.0的方法表layout已經不一樣了。我通過測試確定了其中一些字節的涵義(希望有高手給我一份完整的解釋)如下所示:

00 = Flags
04 = Instance Size
08 = ??
0C = ??
10 = ??
14 = Module addr
18 = Mehtod Table End addr
1C = EEClass addr (泛型類時,這裡的含義不明)
20 = GenericParam Info addr
24 = Gobal Interface Map Table addr(?)
28 = ToString()
2C = Equals()
30 = GetHashCode()
34 = Finalize()
…… 類中的虛方法入口地址
…… 非泛型類的構造函數入口地址
…… interface table addr(如果繼承了接口)
…… 一個四字節,記錄泛型參數個數不知道是否還有其他涵義(如果是泛型類)
…… interface table
…… GenericParam Info(如果是泛型類)

接下來就讓我們看看泛型參數信息的內容:

0x002A3954 002a3958 6c18172c(Type) 6c1808ec(string) 6c180508(object) 00000000 00000000

這裡比較奇怪的是,頭4字節的作用就是跳轉到真實數據。好了讓我來驗證一下(object的方法少就用6c180508來驗證):

!dumpmt -md 6c180508
EEClass: 6bf13ef0
Module: 6bf11000
Name: System.Object
mdToken: 02000002 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 14
--------------------------------------
MethodDesc Table
  Entry MethodDesc   JIT Name
6c0d6a70  6bf54934  PreJIT System.Object.ToString()
6c0d6a90  6bf5493c  PreJIT System.Object.Equals(System.Object)
6c0d6b00  6bf5496c  PreJIT System.Object.GetHashCode()
6c1472f0  6bf54990  PreJIT System.Object.Finalize()
6c1336f0  6bf5492c  PreJIT System.Object..ctor()
6c091d94  6bf54984   NONE System.Object.GetType()
6c091da4  6bf54998   NONE System.Object.MemberwiseClone()
6c09742c  6bf549a4  PreJIT System.Object.FieldSetter(System.String, System.String,
System.Object)
6c09743c  6bf549b0  PreJIT System.Object.FieldGetter(System.String, System.String,
System.Object ByRef)
6c09744c  6bf549bc  PreJIT System.Object.GetFieldInfo(System.String, System.String)
6c091d80  6bf54944   NONE System.Object.InternalEquals(System.Object, System.Object)
6c0d6ab0  6bf54954  PreJIT System.Object.Equals(System.Object, System.Object)
6c0d6ae0  6bf54960  PreJIT System.Object.ReferenceEquals(System.Object, System.Object)
6c091d88  6bf54974   NONE System.Object.InternalGetHashCode(System.Object)

說完了泛型類的參數下面讓就來說說泛型方法的參數。我們來執行Test_Method方法,當進入Test_Method方法後部分主要反匯編及注釋如下:

X tmp = y as X;
00000044 mov     eax,dword ptr [ebp+8]  //得到參數,GenericParam Table的地址
00000047 mov     eax,dword ptr [eax+0Ch] //偏移0Ch處為泛型參數信息地址
0000004a mov     eax,dword ptr [eax]   //得到X的方法表地址,此處X是string

上面的代碼非常好理解我就不再貼內存數據了。通過上面的分析我們可以得出一個結論:泛型方法是通過參數壓棧的方式與泛型參數信息關聯的,而類的泛型參數信息是直接保存在MethodTable中的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved