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

本文探讨了.NET泛型中类型参数的处理方式,通过分析反汇编代码,揭示了CLR如何处理泛型参数。文章以C<T, U, V>类为例,详细解释了Test_Class和Test_Method方法中的泛型类型使用,并展示了对应汇编代码,阐述了泛型类与泛型方法在存储和使用泛型参数时的不同之处。" 110320204,9399872,深度学习:从DNN到正则化和Dropout,"['深度学习', '神经网络', '正则化', '优化算法', '机器学习']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     一位朋友询问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中的。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值