什么是虚方法?

96

为什么要将一个方法声明为“虚拟”的。

使用虚拟的好处是什么?

12个回答

74

Virtual修饰符用于标记一个方法、属性等可以通过使用override修饰符在派生类中进行修改。

示例:

class A
{
    public virtual void Foo()
       //DoStuff For A
}

class B : A
{
    public override void Foo()
    //DoStuff For B

    //now call the base to do the stuff for A and B 
    //if required
    base.Foo()
}

54

虚函数允许继承类替换基类使用的方法。

public class Thingy
{
    public virtual void StepA()
    {
        Console.Out.WriteLine("Zing");
    }

    public void Action()
    {
        StepA();
        Console.Out.WriteLine("A Thingy in Action.");
    }
}

public class Widget : Thingy
{
    public override void StepA()
    {
        Console.Out.WriteLine("Wiggy");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Thingy thingy = new Thingy();
        Widget widget = new Widget();

        thingy.Action();
        widget.Action();

        Console.Out.WriteLine("Press any key to quit.");
        Console.ReadKey();
    }
 }

运行程序后,输出结果将为:

Zing 
A Thingy in Action. 
Wiggy 
A Thingy in Action.

注意,即使 Widget 调用了 Thingy 层次定义的 Action() 方法,Thingy 在内部还是调用了 Widget 的 StepA() 方法。

基本答案是它给了一个类的继承者更多的灵活性。当然,你必须设计好你的类,否则它可能会带来灾难。


30

虚方法是一种方法类型,实际调用的方法取决于底层对象的运行时类型。

非虚方法是一种方法类型,实际调用的方法取决于方法调用点处对象的引用类型。


这应该是一个答案 - 通过修改声明无法定义虚拟。那么它与方法隐藏有什么不同? - SENya

13

MSDN上的虚方法

虚关键字用于修改方法或属性声明,在这种情况下,该方法或属性称为虚成员。虚成员的实现可以被派生类中的重写成员更改。

当调用虚方法时,将检查对象的运行时类型是否有重写成员。调用最派生类中的重写成员,如果没有派生类覆盖了成员,则调用原始成员。(有关运行时类型和最派生实现的更多信息,请参见10.5.3虚方法。)

默认情况下,方法是非虚拟的。您无法重写非虚方法。

以下修饰符不能与虚修改符一起使用:

static abstract override

虚属性的行为类似于抽象方法,除了声明和调用语法上的差异。

  • 在静态属性上使用虚修改符是错误的。
  • 通过包含使用override修改符的属性声明,可以在派生类中重写继承的虚属性。

7
虚方法与基类中的抽象方法类似,不同之处在于在派生类中对其实现是可选的。同时,您可以在虚方法中放置逻辑并在派生类中覆盖这些逻辑。

6

即使您不打算从类中派生出新的类,标记方法为virtual可能是必要的,以便模拟类。一些模拟框架仅允许您模拟虚拟方法。请注意,实现接口的方法隐含地是虚拟的。

我使用RhinoMocks,它有这个限制,并且已经默认将我的方法标记为virtual,就是因为这个原因。对我来说,这可能是使用虚拟方法的最大原因,因为涉及继承的情况要少得多。


4

一个简短的问题,一个简短的答案!

如果你认为你将继承该类,那么将你的方法定义为“virtual”并使其成为“虚函数”。

更长的答案是:“虚函数允许您在派生类中重写该方法,并赋予它另一种含义。”


3
为了能够在派生类中覆盖它。请查看MSDN条目,以获取更深入的解释。

2

在C#中,如果要在派生类中重写基类方法,您必须将基类方法声明为virtual,将派生类方法声明为override,如下所示:

最初的回答:

要在派生类中重写基类方法,在C#中,您需要将基类方法声明为virtual,将派生类方法声明为override,如下所示:

using System;
namespace Polymorphism
{
 class A
 {
 public virtual void Test() { Console.WriteLine("A::Test()"); }
 }

 class B : A
 {
 public override void Test() { Console.WriteLine("B::Test()"); }
 }

 class C : B
 {
 public override void Test() { Console.WriteLine("C::Test()"); }
 }

 class Program
 {
 static void Main(string[] args)
 {

 A a = new A();
 B b = new B();
 C c = new C();
 a.Test(); // output --> "A::Test()"
 b.Test(); // output --> "B::Test()"
 c.Test(); // output --> "C::Test()"

 a = new B();
 a.Test(); // output --> "B::Test()"

 b = new C();
 b.Test(); // output --> "C::Test()"

 Console.ReadKey();
 }
 }
}

您也可以使用虚拟和新关键字混合使用方法隐藏和方法重写,因为派生类的方法可以同时是虚拟和新的。当您想要进一步覆盖派生类方法到下一级时,这是必需的,如下所示:我正在Class C中重写Class B的Test()方法:Original Answer。
using System;
namespace Polymorphism
{
 class A
 {
 public void Test() { Console.WriteLine("A::Test()"); }
 }

 class B : A
 {
 public new virtual void Test() { Console.WriteLine("B::Test()"); }
 }

 class C : B
 {
 public override void Test() { Console.WriteLine("C::Test()"); }
 }

 class Program
 {
 static void Main(string[] args)
 {

 A a = new A();
 B b = new B();
 C c = new C();

 a.Test(); // output --> "A::Test()"
 b.Test(); // output --> "B::Test()"
 c.Test(); // output --> "C::Test()"

 a = new B();
 a.Test(); // output --> "A::Test()"

 b = new C();
 b.Test(); // output --> "C::Test()"

 Console.ReadKey();
 }
 }
}

黄金法则:

虚拟关键字用于修改在基类中声明的方法、属性、索引器或事件,使其可以在派生类中被重写。

覆盖关键字用于将基类的虚拟/抽象方法、属性、索引器或事件扩展或修改为派生类。

新关键字用于隐藏基类的方法、属性、索引器或事件到派生类中。

享受吧 :-)


2
运行时发生在编译时之后。
当你将一个方法声明为虚拟方法时,在派生类中声明该方法需要添加overridenew修饰符。
我们可以看到,当TrySpeak调用时,无论传入父类还是子类,都会调用父类的Speak方法,而TryScream则会调用每个方法。
要理解这一点,有一些事情我们需要知道。在Child类的实例中,有两个来自Child类或Father类的Scream方法。我们可以从Child类或Father类中调用Scream方法。
因为Virtual修饰符标记了该方法可以被派生类重写,这意味着即使从Father类中调用Scream方法,它也会被重写。如果使用new修饰符,则情况会有所不同。
using System;
class Father
{
    Speak()
    {
        Console.Writeline("Father is speaking") 
    }
    virtual Scream()
    {
        Console.Writeline("Father is screaming")    
    }
}
class Child: father
{
    Speak()
    {
        Console.Writeline("Child is speaking")  
    }
    override Scream()
    {
        Console.Writeline("Child is screaming") 
    }
}
class APP
{
    public static void Main()
    {
        // We new two instances here
        Father father = new Father();
        Child child = new Child();        
        // Here we call their scream or speak through TryScream or TrySpeak
        TrySpeak(father);
        TrySpeak(child);
        //>>>"Father is speaking"
        //>>>"Father is speaking"
        TryScream(father);
        TryScream(child);
        //>>>"Father is screaming"
        //>>>"Child is screaming"
    }
    // when your method take an Parameter who type is Father
    // You can either pass in a Father instance or
    // A instance of a derived Class from Father
    // which could be Child
    public static void TrySpeak(Father person)
    {
        person.Speak();
    }
    public static void TryScream(Father person)
    {
        person.Scream();
    }
}

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