【问题标题】:Virtual functions in C#C#中的虚函数
【发布时间】:2011-04-29 11:05:16
【问题描述】:
public class Base1
{
    public virtual void f()
    {
        Console.WriteLine("Base1.f()");
    }
}

public class Derived1 : Base1
{
    // Hides Base1.f() because 'override' was not specified
    public new virtual void f()
    {
        Console.WriteLine("Derived1.f()");
    }
}

public class Derived2 : Derived1
{
    // Overrides Derived1.f()
    public override void f()
    {
        Console.WriteLine("Derived2.f()");

        // Call base method
        base.f();
    }
}

class Program
{
    static void Main(string[] args)
        Base1 ob1 = new Derived1();
        ob1.f();

        Base1 ob2 = new Derived2();
        ob2.f();

        Derived1 ob3 = new Derived2();
        ob3.f();

        Derived2 ob4 = new Derived2();
        ob4.f();
    }
}


// Calls Derived2.f() because Derived2 overrides Derived1().f()
        Derived1 ob3 = new Derived2();
        ob3.f();

期待

Base1 ob2 = new Derived2();
ob2.f();
  1. 派生2函数将被调用,但基类函数被调用 叫,这是什么原因。
  2. .net 是否使用 vtables

【问题讨论】:

  • 请编辑您的问题以适当地格式化它 - 我自己会这样做,但我不知道您最后真正想要做什么。
  • @Jon 请坚持参加婚礼,Jon 一定可以在露营地参加!
  • @David:前段时间看了婚礼。现在回到帐篷里。
  • @Jon 据我所知,它仍在继续。我实际上是穿越大西洋到美国来逃避它,但这里的每一个电视频道都有!美国人只是喜欢你不会相信的英国皇室婚礼。
  • @David:这是我一直在认真思考的问题。我明白为什么英国人可能会对它感兴趣,我也明白为什么英裔美国人可能会关心它。但我根本无法理解为什么一个普通的老美国人会对英国皇室婚礼大发雷霆。我当然不...有一两分钟,我几乎担心这会让我成为一个坏人,但我克服了它。

标签: c# inheritance virtual-functions


【解决方案1】:

编译时静态分析使用的方法槽取决于变量(或表达式)的类型,而不是实际的对象。变量ob2 的类型为Base1,因此使用Base1 方法槽。 then 根据类型选择正确的覆盖(基本上是该插槽上的 vtable)。所以使用了基函数。

要使用 derived2 函数,变量(或表达式)必须输入为Derived1 或子类。

【讨论】:

    【解决方案2】:

    基本上,如果您用来调用f() 的变量的compile-time 类型是Base1,它将调用基本方法——因为实际上并没有覆盖它。 p>

    如果编译时类型是Derived1Derived2,它将根据对象的执行时间类型在Derived1Derived2中调用适当的方法...因为此时编译器只会发出对Derived1.f() 的虚拟调用,并且会在执行时发生覆盖。

    是的,.NET 使用 vtables。

    【讨论】:

      【解决方案3】:

      这里的问题是你混合的东西有点太多了。

      基本上,这就是你所做的:

      1. 你在基类中定义了一个虚方法f
      2. 您从该基类继承,并创建一个 new 虚拟 f 方法
      3. 您从第二类继承,并覆盖 f,这将覆盖第二类中的那个,而不是基类中的那个。

      所以,当你说:

      Base1 b = new Derived2();
      b.f();
      

      那么你总是(在这种情况下)会调用f 的基本实现,因为Derived2 中覆盖的f 是一个不同 f 方法。名称相同,但方法仍然不同。

      原因是编译器会看到你调用的f是来自Base1类的那个,所以它会调用它。

      因为没有类覆盖Base1.f,所以你要调用的就是那个。


      针对评论中的问题,严格来说,该类会有两个虚方法,都命名为f。

      然而,一个被 Derived1 中引入的新的所掩盖。

      您可以在课堂内选择要调用的对象:

      public void MethodInDerived1()
      {
          f();                            // calls Derived1.f()
          base.f();                       // calls Base1.f()
      }
      

      然而,从外部,你需要通过施法来“挑选”。

      换句话说:

      Derived1 d = new Derived1();
      d.f();                              // calls Derived1.f()
      ((Base1)d).f();                     // calls Base1.f()
      

      你也可以通过反射来观察方法。如果你在LINQPad中执行下面的代码,你会看到有两个方法都叫f

      void Main()
      {
          typeof(Derived1).GetMethods().Dump();
      }
      
      public class Base1
      {
          public virtual void f()
          {
              Debug.WriteLine("Base1.f");
          }
      }
      
      public class Derived1 : Base1
      {
          public virtual new void f()
          {
              Debug.WriteLine("Derived1.f");
          }
      }
      
      public class Derived2 : Derived1
      {
          public override void f()
          {
              Debug.WriteLine("Derived2.f");
              base.f();
          }
      }
      

      此脚本的输出,被截断(右侧有更多信息):

      【讨论】:

      • 好吧,严格来说它会有 2 个,但从词汇上讲,你只能引用其中一个,即新的,因为它们具有相同的名称。但是,您可以使用反射找到它们。您也可以调用base.f 来显式选择基本方法。
      • 在 vtables 中如何存储这些信息,因为它有 2 个虚函数
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-12
      相关资源
      最近更新 更多