如何解析非虚拟实例方法继承

本文关键字:实例方法 继承 虚拟 何解析 | 更新日期: 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语义。

回答您编辑的问题:

  1. JIT不能查看任何对象引用,因为它需要静态决定。它查看堆栈上元素的类型,无论该元素来自何处。在可验证的代码中,这是明确的。变量类型不会影响方法绑定(IOW您的问题1无关紧要)
  2. 是的,方法是通过程序集+类型+方法名称和签名(包括返回类型)引用的。非常精确

Richter描述了抖动行为。编译器没有义务依赖它。如果C#编译器有足够的类型信息来可靠地指定基类方法,那么它肯定会让抖动的工作变得更容易。它确实做到了。

事实上,C#编译器必须执行此操作才能实现关键字。基地。如果Foo()方法是虚拟的,则Foo(()调用必须使用基类。抖动没有可靠的方法来解决这个问题,它所拥有的只是派生类的方法表。但是Foo()重写会替换基类中Foo(()方法的方法地址。值得注意的是,dynamic关键字存在问题。

实际上,非虚拟方法是在编译时解析的,因为您可以确定将调用哪个方法。静态方法也是如此。

相反,虚拟方法直到运行时

才能解决