什么是虚拟方法?

时间:2009-03-07 17:01:12

标签: c# virtual keyword

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

使用虚拟有什么好处?

15 个答案:

答案 0 :(得分:45)

Virtual修饰符用于标记可以使用override修饰符在派生类中修改方法\属性(ect)。

示例:

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

答案 1 :(得分:39)

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

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

答案 2 :(得分:18)

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

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

答案 3 :(得分:13)

Virtual Methods on MSDN

  

虚拟关键字用于修改   方法或财产声明,in   方法或财产的情况   被称为虚拟成员。该   虚拟成员的实现可以   由一个压倒一切的成员改变   派生类。

     

当调用虚方法时,   检查对象的运行时类型   对于一个压倒一切的成员。该   覆盖最多的成员   调用类,可能是   原始成员,如果没有派生类   已覆盖该成员。 (更多   关于运行时类型和大多数的信息   派生实现,见10.5.3   虚拟方法。)

     

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

     

您无法使用虚拟修改器   使用以下修饰符:

     

静态 抽象 覆盖

     

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

     
      
  • 在静态属性上使用虚拟修饰符是错误的。
  •   
  • 可以在派生类中重写虚拟继承属性   包括财产声明   使用覆盖修饰符。
  •   

答案 4 :(得分:6)

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

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

答案 5 :(得分:5)

虚方法与基类中的抽象方法类似,只不过它们在派生类上的实现是可选的。您也可以将逻辑放在虚方法中,并在派生类中重写它们。

答案 6 :(得分:3)

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

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

答案 7 :(得分:3)

简短的问题,简短的回答! 如果您认为您将继承其所属的类,则将您的方法限定为“虚拟”。

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

答案 8 :(得分:1)

毋庸置疑,当您的代码尝试遵守Open Closed Principle

时,虚拟方法会派上用场

阅读更多关于开放封闭原则here,鲍勃叔叔的原始OCP白皮书。

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

答案 9 :(得分: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();
      }
    }

答案 10 :(得分:1)

这里用示例C# Virtual Method

清楚地解释了这一点

答案 11 :(得分:1)

运行时在编译时发生 将方法声明为虚拟时,在派生类中声明它需要添加overridenew修饰符。
我们可以看到TrySpeak时。传递给孩子和父亲,两个人都叫父亲的说话,而TryScream,则称为每个方法 要了解这一点,我们应该知道一些事情,在Child的实例中,Child类或父类中有两个Scream方法。我们可以从Child类或Father类调用Scream。 因为Virtaul修饰符标记了方法,所以它可以被派生类覆盖,这意味着即使从父类调用Scream,它也会被覆盖,如果使用new修饰符则它将是不同的。

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

答案 12 :(得分:0)

此链接将通过非常简单的示例为您提供更好的理解 https://stackoverflow.com/a/2392656/3373865

答案 13 :(得分:0)

虚拟方法与非虚拟方法之间的区别。

我们有两个班级;一个是车辆类,另一个是购物车类。 “车辆”类是具有两种方法的基类。一个是虚拟方法“ Speed()”,另一个是非虚​​拟方法“ Average()”。因此,基类虚拟方法“ Speed()”在子类中被覆盖。我们还有一个类“ Program”(执行类),该类具有一个入口点,在该入口处我们创建子类“ Cart”的实例,并将该实例分配给基类“ Vehicle”类型。当我们通过两个类的实例调用虚拟方法和非虚拟方法时,则根据运行类型,实例虚拟方法实现被调用。换句话说,两个类的实例都调用子类重写方法,并且基于该类的实例确定所调用的非虚拟方法。

using System;

namespace VirtualExample
{   
    class Vehicle
    {   
       public double distance=0.0;
       public double hour =0.0;
       public double fuel =0.0; 

       public Vehicle(double distance, double hour, double fuel)
       {
           this.distance = distance;
           this.hour = hour;
           this.fuel = fuel;
       }

       public void Average()
       {
           double average = 0.0;
           average = distance / fuel;
           Console.WriteLine("Vehicle Average is {0:0.00}", average);
       }

       public virtual void Speed()
       {
           double speed = 0.0;
           speed = distance / hour;
           Console.WriteLine("Vehicle Speed is {0:0.00}", speed);
       }
    } 

    class Car : Vehicle
    {
        public Car(double distance, double hour, double fuel)
            : base(distance, hour, fuel)
        {
        }
      public void Average()
        {
            double average = 0.0;
            average = distance / fuel;
            Console.WriteLine("Car Average is {0:0.00}", average);
        }

        public override void Speed()
        {
            double speed = 0.0;           
            speed = distance / hour;
            Console.WriteLine("Car Speed is {0:0.00}", speed);
        }
    } 

    class Program
   {
        static void Main(string[] args)
        {
             double distance,hour,fuel=0.0;
             Console.WriteLine("Enter the Distance");
             distance = Double.Parse(Console.ReadLine());
             Console.WriteLine("Enter the Hours");
             hour = Double.Parse(Console.ReadLine());
             Console.WriteLine("Enter the Fuel");
             fuel = Double.Parse(Console.ReadLine());
             Car objCar = new Car(distance,hour,fuel);
             Vehicle objVeh = objCar;
             objCar.Average();
             objVeh.Average();
             objCar.Speed();
             objVeh.Speed();
            Console.Read();
        }       
    }
}

enter image description here

希望能提供帮助!

答案 14 :(得分:0)

在C#中,要覆盖派生类中的基类方法,必须将基类方法声明为虚拟,将派生类方法声明为重写,如下所示:

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关键字来混合方法隐藏和方法重写。当您要进一步将派生类方法重写到下一个级别时,这是必需的,因为我正在重写C类中的B类,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关键字用于将虚拟/抽象方法,属性,索引器或基类的事件扩展或修改为派生类。

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

享受:-)