对msdn中与虚拟/覆盖相关的示例的小误解
本文关键字:误解 覆盖 msdn 虚拟 | 更新日期: 2023-09-27 18:21:27
在阅读MSDN中的多态性时,我看到了一个虚拟和重写方法的例子:
public class BaseClass
{
public virtual void DoWork() { }
public virtual int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public override void DoWork() { }
public override int WorkProperty
{
get { return 0; }
}
}
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Also calls the new method.
我想知道的是,在什么情况下应该有人这样做?我只是看不出它在任何方面都有用。有人能举一个真实世界的例子吗?
当您想要引用某些对象,并且无法保留其确切类型的引用时,这很有用。例如,如果您有一个混合类型的对象列表:
List<BaseClass> list = new List<BaseClass>() {
new BaseClass(),
new DerivedClass(),
new BaseClass(),
new BaseClass(),
new DerivedClass(),
new DerivedClass()
};
现在您有了BaseClass
引用的列表,但其中一些引用指向DerivedClass
实例。
当你想使用它们时,你不需要检查它们的类型。您只需调用虚拟DoWork
方法,BaseClass.DoWork
方法将被调用用于BaseClass
实例,DerivedClass.DoWork
方法将被呼叫用于DerivedClass
实例:
foreach (BaseClass b in list) {
b.DoWork();
}
你所关心的只是Dancer
会跳舞。如果你提到的是Dancer
,那么你不应该在乎舞者是怎么跳的——只在乎他们是怎么跳。这也是界面背后的想法。
所以,你举办了一个有舞池的派对,你想让人们跳舞。但让他们做他们自己,按照他们想要的方式跳舞,玩得开心。
List<Dancer> danceFloor = new List<Dancer>();
danceFloor.Add(new ReservedDancer());
danceFloor.Add(new SuperFreakDancer ());
public class Dancer
{
public virtual void DoYourDance()
{
// do the robot. Everyone knows that one right?
}
}
public class ReservedDancer : Dancer
{
public override void DoYourDance()
{
// do the waltz
}
}
public class SuperFreakDancer : Dancer
{
public override void DoYourDance()
{
// breakdance !!!
}
}
至于为什么您可能在同一函数中使用多态赋值,请考虑Dancer
工厂方法(您可以将其添加到Dancer
类中,并让DancerType
是描述每个舞者类型的枚举):
public static Dancer NewDancerFromType(DancerType type)
{
Dancer ret = null;
switch (type)
{
case DancerType.Reserved:
ret = new ReservedDancer();
break;
case DancerType.SuperFreak:
ret = new SuperFreakDancer();
break;
}
return ret;
}
有时,也可以说Dancer
是public
,但它的子类是private
。这样,您就可以使用这个工厂方法来获取这些子类的实例,但您仍然会持有Dancer
引用。出于实际目的,我已经实现了好几次该模式,我可以告诉您这一点,但它很长,并且开始脱离本线程的主题。
我认为大多数与面向对象编程和多态性相关的教学/文献的问题是,现实世界中非常缺乏实际有用的例子,可能是因为它们太复杂了。话虽如此,这里还有另一个糟糕的例子:)
基本上,任何多态代码都可以重写为if-else语句。考虑以下代码:
class Car {
public virtual void Drive() {
Console.WriteLine("Driving like a normal car");
}
}
class RaceCar : Car {
public override void Drive() {
Console.WriteLine("Driving really fast!");
}
}
现在,在史前(OO之前),如果我们有一辆赛车,我们会有一些代码可以说:
if (isRaceCar) {
Console.WriteLine("Driving really fast!");
} else {
Console.WriteLine("Driving like a normal car");
}
那么,为什么我们要创建整个类结构,第二段代码似乎要简单得多。
假设我们增加了第三种车型:
class OldCar : Car {
public override void Drive() {
Console.WriteLine("Driving very slowly");
}
}
现在,我们的布尔系统将不再工作,所以我们需要一个开关或其他
switch (carType) {
case RaceCar: ...
case OldCar: ...
case Car: ...
}
好吧,看起来还可以。但现在我们也希望我们的车除了开车之外还能做其他事情,比如GetIn
、GetOut
、Stop
、DoMaintenance
等等。现实世界中的例子可能要大得多。
如果没有多态性,这将导致我们的汽车相关代码分散在各处,并且如果添加一种新型汽车,很容易忘记某个地方的开关情况。使用多态性,特别是使用抽象方法和接口,忘记其中一种情况将导致编译器错误,而不是运行时错误藏错误 需要注意的是,在使用多态性时,这些if/else或switch情况仍然存在,但所有的艰苦工作都是由编译器本身完成的,我们不必太多考虑。 这只是一个例子,还有其他好处,如:Car
并将其发送给其他与汽车相关的代码)。这意味着在添加新型汽车时,您将重用处理汽车的代码