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

时间:2013-07-23 11:15:23

标签: c# inheritance compiler-construction clr il

通过C#从CLR引用,它向我读取,好像call将在运行时通过CLR搜索由基类型定义的方法。

  

然后call IL指令用于调用实例或虚拟实例   方法,您必须指定引用对象的变量。类型   变量本身表示哪种类型定义了该方法   CLR应该打电话。如果变量的类型没有定义方法,   检查基本类型是否匹配方法。

  

当调用非虚拟实例方法时,JIT会找到该类型   对应于用于制作的变量类型的对象   电话。如果类型没有定义被调用的方法,JIT   沿着类层次结构向下寻找此方法。   它可以这样做,因为每个类型对象都有一个引用的字段   到它的基本类型。然后,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所需的变量类型实际上是元数据标记指定的类。

重复警报

实际上它与Is Richter mistaken when describing the internals of a non-virtual method call?

重复

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

3 个答案:

答案 0 :(得分:1)

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

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

回答编辑后的问题:

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

答案 1 :(得分:1)

Richter描述了抖动行为。编译器没有义务依赖它。如果有足够的类型信息可靠地指定基类方法,C#编译器当然可以使抖动的工作更容易。它就是这样。

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

答案 2 :(得分:0)

实际上,非虚拟方法在编译时被解析,因为您确定将调用哪个方法。这同样适用于静态方法。

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