C#中的虚函数

时间:2011-04-29 11:05:17

标签: c# inheritance virtual-functions

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. 将调用derived2函数,但基类函数是 叫什么,原因是什么。
  2. .net是否使用vtables

3 个答案:

答案 0 :(得分:6)

编译期间静态分析使用的方法槽取决于变量(或表达式)的类型,而不是实际对象。变量ob2的类型为Base1,因此使用Base1方法广告位。然后然后根据键入的内容(在该插槽上基本上是vtable)选择正确的覆盖。所以使用了基本功能。

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

答案 1 :(得分:3)

基本上,如果您用来调用f()的变量的编译时类型为Base1,它将调用基本方法 - 因为没有& #39; s实际上覆盖了它。

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

是的,.NET使用vtables。

答案 2 :(得分:3)

这里的问题是你把东西混合得太多了。

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

  1. 您在基类
  2. 中定义虚拟方法f
  3. 您从该基类继承,并创建虚拟f方法
  4. 你从第二个类下来,并覆盖f,这将覆盖第二个类中的那个,而不是基类中的那个。
  5. 所以,当你说:

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

    然后你总是(在这种情况下)会调用f的基本实现,因为f中被覆盖的Derived2不同 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();
        }
    }
    

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

    Output from LINQPad script