所以基本上我想了解C#编译器用来确定被调用的方法是非虚拟实例方法还是虚方法的一般步骤。
混淆来自这两个解释(CLR通过C#第3版,Jeffrey Richter,第4章类型基础)
调用非虚拟实例方法时,JIT编译器会找到 与变量类型对应的类型对象 用于拨打电话
虚拟方法调用
调用虚拟实例方法时,JIT编译器会生成一些 方法中的附加代码,每次都会执行 方法被调用。此代码将首先查看变量 用于拨打电话,然后按地址拨打电话 对象
我创建了小型测试项目
class Program
{
static void Main(string[] args)
{
Parent p = new Derived();
p.Foo(10); // Outputs Derived.Foo(int x)
Derived d = new Derived();
d.Foo(10); // Outputs Derived.Foo(double y)
}
}
internal class Parent
{
public virtual void Foo(int x)
{
Console.WriteLine("Parent.Foo(int x");
}
}
internal class Derived: Parent
{
public override void Foo(int x)
{
Console.WriteLine("Derived.Foo(int x)");
}
public void Foo(double y)
{
Console.WriteLine("Derived.Foo(double y)");
}
}
虽然Jon Skeet有blog post解释为什么程序会产生这些输出,而Eric Lippert在他的blog post中确认了这一点(查看评论部分),我仍然无法弄清楚编译器如何决定被调用的方法是非虚拟实例方法还是虚方法。
对于非虚拟实例方法调用,编译器似乎检查用于调用方法的变量类型,对于虚方法 - 由用于调用方法的变量引用的对象类型,所以我想,在决定如何执行该方法之前,应该有一些方法来确定方法是非虚拟的还是虚拟的。
答案 0 :(得分:10)
在决定如何执行该方法之前,应该有一些方法来确定方法是非虚拟的还是虚拟的。
JIT编译器与C#编译器不同。到执行到达JIT编译器时,C#编译器已经决定是否发出虚方法调用或非虚方法调用。里希特书中唯一告诉你的是JIT编译器调用虚拟方法和非虚方法的不同之处。 not 告诉您C#编译器如何决定是否发出虚拟或非虚方法调用。
通常,为此,您需要查看语言规范。如果编译器将程序代码解释为调用的方法是虚拟的,则它将发出虚拟调用。否则,它不会。在这种特殊情况下语言的规则,以及Jon的帖子,指示编译器在第二次调用中发出对Derived.Foo(double)
的非虚方法调用。这是因为在这种情况下,编译器将程序代码(再次基于语言规范)解释为调用Derived.Foo(double)
。 JIT不知道任何这个,它只是看到IL执行对Derived.Foo(double)
的非虚拟调用,引用d
作为隐式this
参数和10
作为第一个显式参数。