【问题标题】:How c# differentiates between instance methods and virtual methodsc#如何区分实例方法和虚拟方法
【发布时间】:2013-05-27 18:46:15
【问题描述】:

所以基本上我想了解 C# 编译器确定被调用的方法是非虚拟实例方法还是虚拟方法的一般步骤。

混淆来自这两种解释(CLR via C# 3rd edition,Jeffrey Richter,Chapter 4 Type Fundamentals)

当调用一个非虚拟实例方法时,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 中证实了这一点(检查 cmets 部分),但我仍然无法弄清楚编译器如何决定被调用的是非虚拟实例方法或虚拟方法。

似乎对于非虚拟实例方法调用,编译器检查用于调用方法的变量类型,对于虚拟方法 - 用于调用方法的变量引用的对象类型,所以我想,在决定如何执行方法之前,应该有一些方法来确定方法是非虚拟的还是虚拟的。

【问题讨论】:

  • "确定方法是非虚拟还是虚拟" -- 虚拟方法被声明为virtual/override,非虚拟方法不是?
  • 所以我想,应该有某种方法来确定... - 当然有。编译器知道它所使用的类型的所有信息。
  • @dtb 是的,但是 d.Foo() 没有说任何关于虚拟/覆盖的内容。
  • @HenkHolterman 听起来不错,所以编译器会查看 d 变量类型还是查看它引用的对象?
  • 这样想。编译器获得您正在阅读的相同源代码。 能否确定方法调用是指虚拟、实例还是静态方法?你能写出做同样事情的程序吗?然后你可以编写编译器的那部分。编译器的精确程度当然是一个实现细节。

标签: c# .net clr virtual-functions


【解决方案1】:

在决定如何执行方法之前,应该有一些方法来确定方法是非虚拟的还是虚拟的。

JIT 编译器与 C# 编译器不同。当执行到达 JIT 编译器时,C# 编译器已经决定是发出虚拟方法调用还是非虚拟方法调用。 Richter 的书唯一告诉您的是 JIT 编译器在调用虚拟和非虚拟方法时所做的不同事情。 告诉你 C# 编译器如何决定是否发出虚拟或非虚拟方法调用。

通常,为此,您需要查看语言规范。如果编译器将程序代码解释为调用的方法是虚拟的,它将发出一个虚拟调用。否则,它不会。在这种特殊情况下的语言规则,这就是 Jon 的帖子的重点,要求编译器在第二次调用中发出对 Derived.Foo(double) 的非虚拟方法调用。这是因为在这种情况下,编译器将程序代码(再次基于语言规范)解释为调用Derived.Foo(double)。 JIT 对此一无所知,它只看到 IL 执行对 Derived.Foo(double) 的非虚拟调用,引用 d 作为隐式 this 参数,10 作为第一个显式参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-25
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 2015-09-09
    相关资源
    最近更新 更多