如果我在这里犯了错误,请阻止我。
如果我理解正确,当我在类的实例上调用方法时,JIT编译器会找到与实例类型相对应的类型对象,然后在其中找到实际方法代码的引用。
我的问题是这对价值类型有何影响?我的印象是值类型没有像引用类型那样的类型对象指针。如果是这种情况,当调用一个方法代码时,CLR如何设法导航到方法代码?
答案 0 :(得分:7)
考虑一个例子,假设我们有以下结构:
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()
对于ToString
和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是否为值类型无关 或参考类型。使用约束前缀也可以避免 价值类型的潜在版本问题。如果受到约束 不使用前缀,必须根据是否发出不同的IL 或不是值类型覆盖System.Object的方法。例如, 如果值类型V覆盖了Object.ToString()方法,则调用 发出V.ToString()指令;如果没有,则为盒子指令 并发出一个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()
答案 1 :(得分:-1)