通过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?
重复很高兴有另一个人和我有同样的问题。
答案 0 :(得分:1)
引用说明变量的类型,而不是变量引用的类型的类型。变量类型是静态已知的,因此所有决策都是静态的。
C#编译器解析了调用的确切方法并将其编码到IL中。如果引用的程序集不会更改,则JIT本身不必执行任何方法解析。 C#编译器之所以这样做,是因为它想要应用C#语义,而不是CLR语义。
回答编辑后的问题:
答案 1 :(得分:1)
Richter描述了抖动行为。编译器没有义务依赖它。如果有足够的类型信息可靠地指定基类方法,C#编译器当然可以使抖动的工作更容易。它就是这样。
C#编译器实际上必须执行此操作才能实现 base 关键字。如果Foo()方法是虚拟的,则base.Foo()调用必须使用基类。抖动没有可靠的方法来解决这个问题,所有可用的方法都是派生类的方法表。但是Foo()重写会替换基类中Foo()方法的方法地址。值得注意的是a problem与动态关键字。
答案 2 :(得分:0)
实际上,非虚拟方法在编译时被解析,因为您确定将调用哪个方法。这同样适用于静态方法。
相反,虚拟方法直到运行时才能解析