2

通过 C# 从 CLR 引用,它在我看来就像call会在运行时通过 CLR 搜索由基类型定义的方法。

然后callIL指令用于调用实例或虚拟方法,必须指定一个变量来引用一个对象。变量本身的类型指示哪个类型定义了 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需要的变量类型其实就是元数据token指定的类。

重复警报

实际上,在描述非虚拟方法调用的内部时,它是 Richter 错误的重复吗?

很高兴有另一个人和我有同样问题的人。

4

3 回答 3

1

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

C# 编译器实际上必须这样做才能实现base关键字。如果 Foo() 方法是虚拟的,则 base.Foo() 调用必须使用基类。抖动没有可靠的方法来解决这个问题,它可用的只是派生类的方法表。但是 Foo() 覆盖替换了基类中 Foo() 方法的方法地址。值得注意的是动态关键字的问题。

于 2013-07-23T12:09:28.473 回答
1

引用说明的是变量的类型,而不是变量引用的对象实例的类型。变量类型是静态已知的,因此所有决策都是静态的。

C# 编译器解析要调用的确切方法并将其编码到 IL 中。如果引用的程序集没有更改,则 JIT 不必自行进行任何方法解析。C# 编译器这样做是因为它想应用 C# 语义,而不是 CLR 语义。

要回答您编辑的问题:

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

事实上,非虚方法在编译时就被解析了,因为你确定会调用哪个方法。这同样适用于静态方法。

相反,虚方法要到运行时才能解析

于 2013-07-23T11:34:35.567 回答