【发布时间】:2012-08-18 09:41:56
【问题描述】:
我试图了解当对象引用存储在基类变量中时,当对象隐藏基类成员时,CLR 如何正确调度方法调用。
我的困惑点是运行时创建的对象头。堆上的对象头有两个字段:类型指针和同步块索引。类型指针指向类的方法表。即使对象引用是基类的,在堆上创建的对象也是派生类的。这应该导致运行时使用派生类对象的方法表。但是运行时正确调用了基类成员。
能否请您帮助我理解 CLR 在这种情况下如何正确调用方法的流程?
【问题讨论】:
我试图了解当对象引用存储在基类变量中时,当对象隐藏基类成员时,CLR 如何正确调度方法调用。
我的困惑点是运行时创建的对象头。堆上的对象头有两个字段:类型指针和同步块索引。类型指针指向类的方法表。即使对象引用是基类的,在堆上创建的对象也是派生类的。这应该导致运行时使用派生类对象的方法表。但是运行时正确调用了基类成员。
能否请您帮助我理解 CLR 在这种情况下如何正确调用方法的流程?
【问题讨论】:
对象头中记录的对象类型在这里无关紧要。编译器发出方法调用,命名应调用其方法的特定类。在生成的 IL 中非常明显。例如:
class Base {
void foo() { }
void callFoo() {
foo(); // <== here
}
}
class Derived : Base {
new void foo() { }
}
指示的语句生成此 IL:
IL_0002: call instance void ConsoleApplication1.Base::foo()
注意调用操作码中Base的存在,没有歧义。
【讨论】:
调用非虚拟或以任何方式覆盖的方法与方法表无关。 C# 编译器按名称调用该方法(程序集确实包含作为字符串的名称!)并且 JIT 将函数的地址硬编码到发出的 x86 代码中。该地址不依赖于this 对象引用的运行时类型。
【讨论】: