什么是虚拟方法?


Answers:


58

所述虚拟改性剂是用于标记的方法\属性(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()
}

43

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

基本的答案是它给类的继承者更大的灵活性。当然,您必须精心设计课程,否则可能会造成严重破坏。


23

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

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


这应该是一个答案-不能通过声明的修改条款来定义虚拟。与隐藏方法有什么不同?
SENya '19

13

MSDN上的虚拟方法

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

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

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

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

静态 抽象 覆盖

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

  • 在静态属性上使用虚拟修饰符是错误的。
  • 可以通过包含使用override修饰符的属性声明来在派生类中覆盖虚拟继承的属性。

6

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

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


5

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



3

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

更长的答案:“虚拟使您可以重写,从而在派生类中赋予方法另一种含义。


1

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

在此处阅读有关开放式封闭原则的更多信息,这是Bob叔叔的原始OCP白皮书。

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



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

“不存在”是什么意思?你可以提供一个参考
穆巴拉克

这看起来不像C#继承。在 public之后的访问修饰符class Aclass B原因编译时错误。来自派生类的基类中成员的可访问性是从基类中单独指定的(默认成员是private)。
Minh Tran

@Minh Tran-是的,您是对的。那就是c ++继承。无论如何,我编辑了帖子。
Lineesh K Mohan

1

运行时发生在编译时。
在将方法声明为虚拟方法时,在派生类中对其进行声明要求您添加overridenew修饰符。
我们可以看到的时候TrySpeak。传入孩子和父亲,都调用Speak of父亲,而TryScream则将调用每种方法。
为了理解这一点,我们应该了解一些事情,在Child的实例中,ScreamChild类或父亲类有两种方法。我们可以Scream 从儿童班或父亲班召集。因为VirtaulModifier标记了方法,所以它可以被派生类覆盖,这意味着即使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();
    }
}

1

在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关键字来混合方法隐藏和方法重写,因为派生类的方法可以同时是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关键字用于修改在基类中声明的方法,属性,索引器或事件,并允许其在派生类中被覆盖。

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

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

请享用 :-)


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.