84

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

使用虚拟有什么好处?

4

13 回答 13

63

Virtual Modifier 用于标记方法\property(ect) 可以在派生类中使用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()
}
于 2009-03-07T17:02:46.073 回答
47

Virtual 允许继承类替换基类随后使用的方法。

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() 方法。

基本答案是它为类的继承者提供了更大的灵活性。当然,你必须很好地设计你的课程,否则它可能会造成严重破坏。

于 2009-10-02T20:57:41.393 回答
26

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

非虚拟方法是一种方法,其中调用的实际方法取决于方法调用时对象的引用类型。

于 2009-03-07T20:03:29.273 回答
13

MSDN 上的虚拟方法

virtual 关键字用于修改方法或属性声明,在这种情况下,方法或属性称为虚拟成员。虚拟成员的实现可以通过派生类中的覆盖成员进行更改。

调用虚拟方法时,将检查对象的运行时类型以查找覆盖成员。调用最派生类中的重写成员,如果没有派生类重写该成员,则该成员可能是原始成员。(有关运行时类型和大多数派生实现的更多信息,请参阅 10.5.3 虚拟方法。)

默认情况下,方法是非虚拟的。您不能覆盖非虚拟方法。

您不能将 virtual 修饰符与以下修饰符一起使用:

静态 抽象 覆盖

虚拟属性的行为类似于抽象方法,除了声明和调用语法不同。

  • 在静态属性上使用 virtual 修饰符是错误的。
  • 通过包含使用 override 修饰符的属性声明,可以在派生类中覆盖虚拟继承属性。
于 2009-03-07T17:04:38.877 回答
6

虚拟方法类似于基类中的抽象方法,只是它们在派生类上的实现是可选的。您也可以将逻辑放在虚拟方法中并在派生类中覆盖它们。

于 2009-03-07T17:04:22.137 回答
6

即使您不打算从该类派生,也可能需要将方法标记为 virtual 以模拟该类。一些模拟框架只允许你模拟虚拟方法。请注意,实现接口的方法是隐式虚拟的。

我使用具有此限制的 RhinoMocks,并且出于这个原因默认将我的方法标记为虚拟。对我来说,这可能是使用虚拟方法的最大原因,因为继承发挥作用的情况要少得多。

于 2009-03-07T17:06:14.363 回答
4

一个简短的问题,一个简短的答案!如果您认为您将继承它所属的类,请将您的方法限定为“虚拟”。

更长的答案:“虚拟使您能够覆盖,以在派生类中赋予您的方法的另一种含义。

于 2012-12-30T23:22:13.147 回答
3

为了能够在继承类中覆盖它。

查看关键字的MSDN 条目。这更深入地解释了它。

于 2009-03-07T17:04:20.500 回答
2

在 C# 中,为了在派生类中重写基类方法,您必须将基类方法声明为虚拟,并将派生类方法声明为 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();
 }
 }
}

您还可以使用 virtual 和 new 关键字混合方法隐藏和方法覆盖,因为派生类的方法可以同时是 virtual 和 new。当您想进一步将派生类方法覆盖到下一个级别时,这是必需的,因为我正在覆盖 Class B,Class C 中的 Test() 方法,如下所示:

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();
 }
 }
}

金字:virtual 关键字用于修改基类中声明的方法、属性、索引器或事件,并允许在派生类中覆盖它。

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

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

请享用 :-)

于 2019-02-20T07:09:38.947 回答
1

不用说,当您的代码试图遵守开放封闭原则时,虚拟方法会派上用场

在此处阅读有关 Open Closed 原则的更多信息,鲍勃叔叔的原始 OCP 白皮书。

另请注意,与 Java 不同,C# 中的方法默认不是虚拟的。

于 2009-03-07T17:10:58.330 回答
1

虚函数是实际上并不存在的函数。派生类可以通过重写来修改虚函数。虚函数是实现运行时多态性的方法之一

    public class sample {
      public virtual void fun(){
        Console.WriteLine("base sample class \n");
      }
    }
    public class A : sample{
      public override void fun(){
        Console.WriteLine("Class A \n");
      }
    }
    public class B : sample{
      public override void fun(){
        Console.WriteLine("Class B \n");
      }
    }
    class run{
      public static void main(String[] args){
        sample obj = new sample();
        sample obj1 = new A();
        sample obj2 = new B();
        obj.fun();
        obj1.fun();
        obj2.fun();
      }
    }
于 2015-03-31T04:30:49.597 回答
1

这里用示例C# Virtual Method清楚地解释了它

于 2017-01-26T16:28:47.787 回答
1

运行时发生在编译时。
当您将方法声明为虚拟时,在派生类中声明它需要您添加overrideornew修饰符。
我们可以看到当TrySpeak. 传入child和father,都调用Speak of Father,while TryScream,会调用各个方法。
要理解这一点,我们应该知道一些事情,在 Child 的实例中,有Scream来自 Child 类或 Father 类的两种方法。我们可以调用Scream from Child 类或 Father 类。因为Virtaul修饰符标记了方法,所以它可以被派生类覆盖,这意味着即使Scream是从父类调用的,它也是被覆盖的,如果使用新修饰符会有所不同。

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.Scream();
    }
    public static void TryScream(Father person)
    {
        person.Speak();
    }
}
于 2017-05-23T17:43:13.437 回答