为什么要将一个方法声明为“虚拟”的。
使用虚拟的好处是什么?
虚函数允许继承类替换基类使用的方法。
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() 方法。
基本答案是它给了一个类的继承者更多的灵活性。当然,你必须设计好你的类,否则它可能会带来灾难。
虚方法是一种方法类型,实际调用的方法取决于底层对象的运行时类型。
非虚方法是一种方法类型,实际调用的方法取决于方法调用点处对象的引用类型。
虚关键字用于修改方法或属性声明,在这种情况下,该方法或属性称为虚成员。虚成员的实现可以被派生类中的重写成员更改。
当调用虚方法时,将检查对象的运行时类型是否有重写成员。调用最派生类中的重写成员,如果没有派生类覆盖了成员,则调用原始成员。(有关运行时类型和最派生实现的更多信息,请参见10.5.3虚方法。)
默认情况下,方法是非虚拟的。您无法重写非虚方法。
以下修饰符不能与虚修改符一起使用:
static abstract override
虚属性的行为类似于抽象方法,除了声明和调用语法上的差异。
- 在静态属性上使用虚修改符是错误的。
- 通过包含使用override修改符的属性声明,可以在派生类中重写继承的虚属性。
即使您不打算从类中派生出新的类,标记方法为virtual可能是必要的,以便模拟类。一些模拟框架仅允许您模拟虚拟方法。请注意,实现接口的方法隐含地是虚拟的。
我使用RhinoMocks,它有这个限制,并且已经默认将我的方法标记为virtual,就是因为这个原因。对我来说,这可能是使用虚拟方法的最大原因,因为涉及继承的情况要少得多。
一个简短的问题,一个简短的答案!
如果你认为你将继承该类,那么将你的方法定义为“virtual”并使其成为“虚函数”。
更长的答案是:“虚函数允许您在派生类中重写该方法,并赋予它另一种含义。”
在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();
}
}
}
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
修饰符。TrySpeak
调用时,无论传入父类还是子类,都会调用父类的Speak
方法,而TryScream
则会调用每个方法。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();
}
}