如果派生类没有重写该方法,应该调用哪个版本?

6

我正在努力理解C#中重写(override)和虚拟(virtual)的必要性,因此我编写了以下代码:

using System;
namespace Override
{
    class Base 
    {
        public virtual void method() 
        {
            Console.WriteLine("Base method");
        }
    }
    class Derived : Base 
    {
        public override void method()
        {
            Console.WriteLine("Derived method");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Derived d = new Derived();
            d.method();
        }
    }
}

我原本期望"派生方法"会被调用并打印输出。然后我编写了以下代码,未使用virtual/override组合。

using System;
namespace Override
{
    class Base 
    {
        public void method() 
        {
            Console.WriteLine("Base method");
        }
    }
    class Derived : Base 
    {
        public void method()
        {
            Console.WriteLine("Derived method");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Derived d = new Derived();
            d.method();
        }
    }
}

我得到了相同的结果,即“派生方法”被调用并打印。我的问题是,如果代码在没有虚拟/覆盖的情况下按照我的预期工作,那么它们的需要是什么?或者我在这里错过了什么?


在第二个中,你没有收到有关隐藏方法的警告吗?“'Derived.Method()' 隐藏了继承成员 Base.Method()”。如果打算隐藏,请使用 new 关键字。 - Sayse
谢谢大家的回答,现在我对这个概念很清楚了。 - ZoomIn
6个回答

12

在您的源代码中,您总是进行简单的继承而没有任何多态行为。您总是创建派生类的实例并将其分配给派生类实例变量。

DerivedClass d = new DerivedClass(); // here no polymorphism, and only inheritance is there

当您使用类变量调用方法时,它将始终调用DerivedClass方法,无论父类中的方法是否为虚拟方法。

在多态性中,您的程序不知道您要调用方法的类的确切类型(这个概念被称为迟绑定)。 如下面的示例所示:

BaseClass b = new DerivedClass(); // here b is a base class instance but initiated using derived class

调用b.method()后,它将执行晚期绑定并显示多态行为(仅当该方法在基类中设置为虚拟时)

注意:virtual关键字将绑定延迟到运行时的正确版本方法,是实现多态性的核心关键词。因此,为了达到精确的多态行为,请在父类中将方法声明为虚拟,在子类中覆盖该方法。


谢谢 - 对我来说,关键词是“延迟绑定”。 - Eccountable

8

virtual关键字允许在运行时根据编译时无法获取的信息选择正确版本的方法。考虑对您示例的以下微调:

using System;
namespace Override
{
    class Base 
    {
        public virtual void method() 
        {
            Console.WriteLine("Base method");
        }
    }
    class Derived : Base 
    {
        public override void method()
        {
            Console.WriteLine("Derived method");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Derived d = new Derived();
            Base b = d;
            b.method();
        }
    }
}

使用 virtual/override,此代码将显示 Derived method,因为在运行时我们可以看到 b 确实是一个 Derived 实例。如果没有 virtual/override,它将显示 Base method,因为 b 的声明类型是 Base


3

以下是您缺失的测试:

Base d = new Derived();
d.method(); // "Derived method"

Base b = new Base();
b.method(); // "Base method"

此外,想象一下,如果您有一个由不同继承对象组成的Base对象集合。使用virtual关键字,这些Base对象可以在运行时理解它们真正的类型。
List<Base> collection = new List<Base>();
collection.Add(new Base());
collection.Add(new Derived()};
collection.Add(new Base());

foreach(Base b in collection)
{
     b.method(); // will print out "Base" or "Derived" correctly
}

2

看到这个DIFFERENCE

        class Base 
        {
            public void method() 
            {
                Console.WriteLine("Base method");
            }
        }
        class Derived : Base 
        {
            public void method()
            {
                Console.WriteLine("Derived method");
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Derived d;

                d = new Derived();
                d.method();

                d = new Base();
                d.method();
            }
        }

输出

派生方法
派生方法
            class Base 
            {
                public virtual void method() 
                {
                    Console.WriteLine("Base method");
                }
            }
            class Derived : Base 
            {
                public override void method()
                {
                    Console.WriteLine("Derived method");
                }
            }
            class Program
            {
                static void Main(string[] args)
                {
                    Derived d;

                    d = new Derived();
                    d.method();

                    d = new Base();
                    d.method();
                }
            }

输出

派生方法

基础方法


1
那甚至无法编译,无法将d设置为新的Base()。你的示例试图做与Chowletts答案相同的事情。 - Sayse

2

基类指针可以用来指向基类对象或任何派生自基类的对象。因此,在基类对象指向派生类时,需要使用虚方法。

Base d = new Derived();
d.method(); // "Derived method"

0

Derived类中的'method'方法将隐藏Base类的实现,这就是为什么你会收到“Derived method”消息的原因。

虚拟和抽象有许多用途,但其中一个例子是在基类中具有某些功能,可能不适用于从基类继承的所有类的情况。使用虚拟允许另一个类完全覆盖该功能并提供自己的实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接