对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.

我想知道的是,在什么情况下应该有人这样做?我只是看不出它在任何方面都有用。有人能举一个真实世界的例子吗?

对msdn中与虚拟/覆盖相关的示例的小误解

当您想要引用某些对象,并且无法保留其确切类型的引用时,这很有用。例如,如果您有一个混合类型的对象列表:

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

有时,也可以说Dancerpublic,但它的子类是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: ...
}

好吧,看起来还可以。但现在我们也希望我们的车除了开车之外还能做其他事情,比如GetInGetOutStopDoMaintenance等等。现实世界中的例子可能要大得多。

如果没有多态性,这将导致我们的汽车相关代码分散在各处,并且如果添加一种新型汽车,很容易忘记某个地方的开关情况。使用多态性,特别是使用抽象方法和接口,忘记其中一种情况将导致编译器错误,而不是运行时错误藏错误

需要注意的是,在使用多态性时,这些if/else或switch情况仍然存在,但所有的艰苦工作都是由编译器本身完成的,我们不必太多考虑。

这只是一个例子,还有其他好处,如:

  • 能够在不更改原始代码的情况下扩展代码(例如,其他人创建自己的Car并将其发送给其他与汽车相关的代码)。这意味着在添加新型汽车时,您将重用处理汽车的代码
  • 允许相同行为的微小变化(例如,我想要一辆普通的车,但我只希望它在有人驾驶时显示额外的信息)