1

在下面的示例中,我在值类型 ( ) 上调用虚拟方法int

namespace ShortTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 42;
            i.ToString();
            ((object)i).ToString();
        }
    }
}

查看 Reflector 中生成的 CIL,我可以看到以下两个调用的代码:

.locals init ([0] int32 i, ...)
...
L_001a: ldloca.s i
L_001c: call instance string [mscorlib]System.Int32::ToString()
L_0021: pop
L_0022: ldloc.0
L_0023: box int32
L_0028: callvirt instance string [mscorlib]System.Object::ToString()
L_002d: pop
...

this参数在第一种情况下是指向 int 的托管指针,在第二种情况下是对装箱的引用int(即,指向具有标头字段和 int 字段的对象的指针)。

由于两个调用(int实现ToString())使用相同的方法,这如何工作?该System.Int32::ToString()方法使用ldind.i4指针this来检索int' 值,因此它应该int在第一种情况下获取 ' 值,但在第二种情况下它应该获取装箱int的第一个字段(标题)的值:

L_0000: ldarg.0
L_0001: ldind.i4
...
4

1 回答 1

3

但它应该在第二种情况下获得装箱 int 的第一个字段(标题)的值

您假设该ToString方法传递了一个指针,并且恰好指向的任何字节都是被读取的。这种假设是错误的。

值类型的实例方法的行为就像它有一个ref T“this”参数。无论值类型是否被装箱,.NET 运行时都将确保正确传递它,因此该方法不需要对装箱值进行任何特殊处理。

你的出发点是正确的:

I.8.9.7 值类型定义

当在值类型上调用非静态方法(即实例或虚拟方法)时,它的 this 指针是对实例的托管引用,而当在关联的装箱类型上调用方法时,this 指针是对象引用。

但是,这种情况发生的方式是相同的ldind.i4指令无论如何都可以工作。只有当它引用非托管指针时,它才会简单地读取字节。

你发现了

II.13.3 值类型的方法

类的实例和虚拟方法应被编码为期望对类实例的引用作为 this 指针。相比之下,值类型的实例和虚方法应被编码为期望一个托管指针(参见第 I 部分)指向该值类型的未装箱实例。当装箱值类型作为 this 指针传递给其实现由未装箱值类型提供的虚方法时,CLI 应将装箱值类型转换为指向未装箱值类型的托管指针。

乍一看与上述内容相矛盾,但实际上它说实现必须简单地使用任何必要的魔法来使其工作。

于 2013-02-28T10:14:24.900 回答