5

如果我在这里犯了错误,请阻止我。

如果我理解正确的话,当我在类的实例上调用方法时,JIT 编译器会定位到与实例类型对应的类型对​​象,然后在其中定位对实际方法代码的引用。

我的问题是这对值类型有何作用?我的印象是值类型没有像引用类型那样的类型对象指针。如果是这种情况,当调用一个方法代码时,CLR 如何设法导航到方法代码?

4

2 回答 2

8

考虑一个例子,假设我们有以下结构:

public struct Test
{
    public void TestMethod()
    {            
    }
}

这是它的IL代码:

.class public sequential ansi sealed beforefieldinit ConsoleApplication.Test
    extends [mscorlib]System.ValueType
{
    .pack 0
    .size 1

    .method public hidebysig 
        instance void TestMethod () cil managed 
    {
        // Method begins at RVA 0x21dc
        // Code size 1 (0x1)
        .maxstack 8

        IL_0000: ret
    } // end of method Test::TestMethod

}

好的,现在因为 C# 编译器静态知道 的类型Test,它可以进行重载解析并找到TestMethod被调用的确切内容。然后它发出 MSIL 以将参数推送到 MSIL 虚拟堆栈上,它需要一个指针类型的参数,Test编译器无需装箱即可处理该参数,并发出包含对该特定方法的元数据引用的调用指令。

.locals init (
        [0] valuetype ConsoleApplication.Test test
    )

IL_0000: ldloca.s test
IL_0002: initobj ConsoleApplication.Test
IL_0008: ldloca.s test
IL_000a: call instance void ConsoleApplication.Test::TestMethod()

ForToString并且GetHashCode编译器使用Constrained OpCode,因为这些方法可以重载。

IL_0002: initobj ConsoleApplication.Test
IL_0008: ldloca.s test
IL_000a: constrained. ConsoleApplication.Test
IL_0010: callvirt instance int32 [mscorlib]System.Object::GetHashCode()

受约束的操作码允许 IL 编译器以统一的方式调用虚函数,而与 ptr 是值类型还是引用类型无关。使用受约束的前缀还可以避免值类型的潜在版本控制问题。如果不使用约束前缀,则必须根据值类型是否覆盖 System.Object 的方法来发出不同的 IL。例如,如果值类型 V 覆盖 Object.ToString() 方法,则会发出 call V.ToString() 指令;如果没有,则会发出一个 box 指令和一个 callvirt Object.ToString() 指令。如果稍后删除覆盖,则在前一种情况下可能会出现版本控制问题,如果稍后添加覆盖,则在后一种情况下会出现版本控制问题。

因为该GetType方法需要装箱,因为它是非虚拟的并且在Object类型中定义。

IL_0002: initobj ConsoleApplication.Test
IL_0008: ldloc.0
IL_0009: box ConsoleApplication.Test
IL_000e: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
于 2013-08-06T09:19:58.983 回答
-1

它称为装箱或自动装箱,如果您在其上调用某个方法,CLR 将自动从值类型实例化相应的类。

更多关于这里这里

于 2013-08-06T08:32:51.990 回答