对值类型调用方法
本文关键字:方法 调用 类型 | 更新日期: 2023-09-27 18:26:37
如果我在这里犯了错误,请阻止我。
如果我理解正确,当我在类的实例上调用方法时,JIT编译器会定位与实例类型相对应的类型对象,然后在其中定位对实际方法代码的引用。
我的问题是,这如何适用于值类型?我的印象是,值类型不像引用类型那样有类型对象指针。如果是这样的话,CLR如何在调用方法代码时导航到方法代码?
考虑一个例子,假设我们有以下结构:
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
,编译器使用受约束的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()指令;如果没有,则为box指令和callvirt Object.ToString()指令。A.如果覆盖是稍后删除,在后一种情况下,如果稍后添加了覆盖。
对于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()
它被称为装箱或自动装箱,如果您在值类型上调用某个方法,CLR将自动实例化相应的类。
更多关于这里和这里