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

时间:2014-09-10 14:38:33

标签: c# method-overriding

在MSDN中阅读about polymorphism时, 我看到了一个虚拟和重写方法的例子:

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.

我想知道的是,在什么情况下应该有人这样做? 我无法以任何方式看到它是如何有用的。有人可以举一个现实世界的例子吗?

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

你关心的是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个引用。为了实际的目的,我已经多次实现了这种模式,我可以告诉你这个但是很长时间并开始摆脱这个主题的主题。

答案 2 :(得分:2)

我认为与面向对象编程和多态性相关的大多数教学/文献的问题在于,实际上有用的实际例子很少,可能是因为它们太复杂了。话虽如此,这是另一个糟糕的例子:)

基本上,任何多态代码都可以重写为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并将其发送到其他代码来处理与汽车有关的代码)。这意味着您在添加新型汽车时会重复使用处理汽车的代码。
  • 允许相同行为的小变化(例如,我想要一辆普通的汽车,但我只希望它在有人驾驶时显示额外的消息)。