如何解析非虚拟实例方法继承
本文关键字:实例方法 继承 虚拟 何解析 | 更新日期: 2023-09-27 18:24:19
通过C#从CLR引用,在我看来,call
将在运行时通过CLR搜索由基类型定义的方法。
然后使用
call
IL指令调用实例或虚拟方法,则必须指定一个引用对象的变量。类型的值表示哪个类型定义的方法CLR应该调用。如果变量的类型没有定义方法,检查基类型是否有匹配的方法。
和
当调用非虚拟实例方法时,JIT会定位类型对象,该对象对应于要生成的变量的类型呼叫。如果类型没有定义要调用的方法,JIT沿着类层次结构向下走向Object,查找此方法。它之所以能够做到这一点,是因为每个类型对象中都有一个引用到其基本类型。然后,JIT在类型对象的方法表,该表引用被调用的方法。
然而,根据以下示例,似乎在编译时检查了方法继承:
class A
{
public void Foo() {}
}
class B : A {}
void Main()
{
new B().Foo();
}
IL_0000: newobj UserQuery+B..ctor
IL_0005: call UserQuery+A.Foo // Not B.Foo, resolved by C# complier.
我说得对吗?
即使我这样做:
void Main()
{
B x = new B();
x.Foo();
}
IL_0000: newobj UserQuery+B..ctor
IL_0005: stloc.0 // x
IL_0006: ldloc.0 // x
IL_0007: callvirt UserQuery+A.Foo // Not B.Foo, resolved by C# complier.
更新:
现在我明白了,决议是静态的。
我相信JIT需要的变量类型实际上是元数据令牌指定的类。
重复警报
事实上,它与Richter在描述非虚拟方法调用的内部时出错了吗?
很高兴有另一个人和我有同样的问题。
引号表示变量的类型,而不是变量引用的对象实例类型。变量类型是静态已知的,因此所有决策都是静态的。
C#编译器解析要调用的确切方法,并将其编码到IL中。如果引用的程序集没有更改,JIT就不必自己进行任何方法解析。C#编译器这样做是因为它希望应用C#语义,而不是CLR语义。
回答您编辑的问题:
- JIT不能查看任何对象引用,因为它需要静态决定。它查看堆栈上元素的类型,无论该元素来自何处。在可验证的代码中,这是明确的。变量类型不会影响方法绑定(IOW您的问题1无关紧要)
- 是的,方法是通过程序集+类型+方法名称和签名(包括返回类型)引用的。非常精确
Richter描述了抖动行为。编译器没有义务依赖它。如果C#编译器有足够的类型信息来可靠地指定基类方法,那么它肯定会让抖动的工作变得更容易。它确实做到了。
事实上,C#编译器必须执行此操作才能实现基关键字。基地。如果Foo()方法是虚拟的,则Foo(()调用必须使用基类。抖动没有可靠的方法来解决这个问题,它所拥有的只是派生类的方法表。但是Foo()重写会替换基类中Foo(()方法的方法地址。值得注意的是,dynamic关键字存在问题。
实际上,非虚拟方法是在编译时解析的,因为您可以确定将调用哪个方法。静态方法也是如此。
相反,虚拟方法直到运行时