[英]Why virtual calls on value types work with pointers to the value and boxed values?
在下面的示例中,我在值类型( 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的托管指针,以及第二种情况下对boxed int
的引用(即指向带有header字段和int字段的对象的指针)。
由于两个调用使用相同的方法( int
实现ToString()
),这怎么办? System.Int32::ToString()
方法在this
指针上使用ldind.i4
来检索int
的值,所以它应该在第一种情况下得到int
的值,但它应该得到盒装int
的值在第二种情况下的第一个字段(标题):
L_0000: ldarg.0
L_0001: ldind.i4
...
但它应该在第二种情况下得到boxed int的第一个字段(标题)的值
您假设ToString
方法传递了一个指针,而且恰好指向的字节是读取的内容。 这个假设是错误的。
值类型的实例方法的行为类似于具有ref T
“this”参数。 无论值类型是否已装箱,.NET运行时都会确保正确传递,因此该方法不需要为盒装值提供任何特殊处理。
你的出发点是正确的:
I.8.9.7值类型定义
当在值类型上调用非静态方法(即实例或虚方法)时,其this指针是对实例的托管引用,而当在关联的盒装类型上调用该方法时,this指针是对象参考。
但是,这种情况发生的方式是相同的ldind.i4
指令无论如何都有效。 只有当它引用非托管指针时,它才会读取字节。
你发现了
II.13.3价值类型的方法
应该对类的实例和虚方法进行编码,以期望对该类的实例的引用作为该指针。 相比之下,值类型的实例和虚方法应编码为期望托管指针(请参阅分区I)到值类型的未装箱实例。 当盒装值类型作为this指针传递给虚拟方法时,CLI应将盒装值类型转换为指向未装箱值类型的托管指针,该方法的实现由未装箱的值类型提供。
乍一看与上述内容相矛盾,但实际上说实施必须简单地使用任何必要的魔法才能使其发挥作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.