关于c#:什么是虚拟方法?

What are Virtual Methods?

为什么要将方法声明为"virtual"。

使用虚拟的好处是什么?


虚修饰符用于标记可以使用重写修饰符在派生类中修改方法属性(ECT)。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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();
    }
 }

运行程序时,输出将为:

1
2
3
4
Zing
A Thingy in Action.
Wiggy
A Thingy in Action.

注意,即使小部件调用了thingy级别定义的action()方法,thingy内部也调用了小部件的stepa()方法。

基本的答案是它给类的继承者更多的灵活性。当然,你必须把你的课程设计得很好,否则会造成轻微的破坏。


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

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


msdn上的虚拟方法

The virtual keyword is used to modify
a method or property declaration, in
which case the method or the property
is called a virtual member. The
implementation of a virtual member can
be changed by an overriding member in
a derived class.

When a virtual method is invoked, the
run-time type of the object is checked
for an overriding member. The
overriding member in the most derived
class is called, which might be the
original member, if no derived class
has overridden the member. (For more
information on run-time type and most
derived implementation, see 10.5.3
Virtual methods.)

By default, methods are non-virtual.
You cannot override a non-virtual
method.

You cannot use the virtual modifier
with the following modifiers:

static abstract override

Virtual properties behave like
abstract methods, except for the
differences in declaration and
invocation syntax.

  • It is an error to use the virtual modifier on a static property.
  • A virtual inherited property can be overridden in a derived class by
    including a property declaration that
    uses the override modifier.

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

我使用的Rhinomocks有这个限制,并且由于这个原因,默认情况下会将我的方法标记为虚拟的。对于我来说,这可能是使用虚拟方法的最大原因,因为继承的使用频率要低得多。


虚拟方法与基类中的抽象方法类似,只是它们在派生类上的实现是可选的。还可以将逻辑放入虚拟方法中,并在派生类中重写这些逻辑。


一个简短的问题,一个简短的回答!如果您认为将继承方法所属的类,请将其限定为"virtual"。

一个更长的答案:"virtual使您能够重写派生类中方法的另一个含义。


以便能够在继承类中重写它。

查看msdn条目中的关键字。这就更深入地解释了这一点。


运行时在编译时发生。当您将一个方法声明为虚方法时,在派生类中声明它需要添加一个overridenew修饰符。我们可以在TrySpeak时看到。传子传父,都叫父,以东传1〔3〕都叫每一种方法。为了理解这一点,有一些事情我们应该知道,在一个儿童的例子中,有两个来自儿童类或父亲类的Scream方法。我们可以从儿童班或父亲班叫Scream。因为Virtaul修饰符标记方法以便它可以被派生类重写,这意味着即使从父类调用Scream,它也是重写的,如果使用新的修饰符,它将是不引用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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();
    }
}

这里用示例C虚拟方法清楚地解释了这一点。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    public class sample {
      public virtual void fun(){
        Console.WriteLine("base sample class
"
);
      }
    }
    public class A : sample{
      public override void fun(){
        Console.WriteLine("Class A
"
);
      }
    }
    public class B : sample{
      public override void fun(){
        Console.WriteLine("Class B
"
);
      }
    }
    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();
      }
    }


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

在这里阅读更多关于开放-关闭原则的内容,Bob叔叔最初的OCP白皮书。

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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()方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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();
 }
 }
}

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

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

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

享受:-


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

我们有两个班,一个是汽车班,另一个是手推车班。"vehicle"类是具有两种方法的基类;一种是虚拟方法"speed()",另一种是非虚拟方法"average()"。因此,基类虚拟方法"speed()"在子类中被重写。我们还有一个类"程序"(执行类),它有一个入口点,我们在这里创建一个子类"cart"的实例,该实例被分配给基类"vehicle"类型。当我们通过两个类的实例调用虚方法和非虚方法时,然后根据运行类型调用实例虚方法实现;换句话说,两个类的实例都调用子类重写方法,并且调用的非虚方法是基于类的实例确定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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

希望对大家有所帮助!


这个链接将为您提供一个非常简单的示例,让您更好地理解https://stackoverflow.com/a/2392656/3373865