虫虫技术在线--技术决定出路

当前位置: 首页 > 编程 > .net >

简单分析.net泛型中的类型参数

时间:2010-02-19 14:19来源:未知 作者:admin 点击:
一位朋友询问tppeof、GetType()、is、as的问题,在实验的时候顺手就用泛型写的例子。在看Jit后的反汇编时发现了一个问题,clr对泛型参数有些特殊处理。上网查了一下没有找到介绍泛型参

  一位朋友询问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:WindowsassemblyGAC_32mscorlib2.0.0.0__b77a5c561934e089mscorlib.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中的。

 

(责任编辑:admin)
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
推荐内容