多态性和铸造

时间:2014-04-14 23:34:21

标签: c# polymorphism override virtual

我想了解c#中的多态性,所以通过尝试几个结构,我提出了以下案例:

class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Shape.Draw()");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Circle.Draw()");
    }
}

我理解为了将Draw()消息发送到几个相关对象,所以他们可以根据自己的实现行事我必须更改(在这种情况下)形状'指向'的实例:

Shape shape = new Circle();
shape.Draw(); //OK; This prints: Circle.Draw()

但是,为什么,当我这样做时:

Circle circle = new Circle();
circle.Draw(); //OK; This prints: Circle.Draw()

Shape shape = circle as Shape; // or Shape shape = (Shape)circle;
shape.Draw();

打印:“Circle.Draw()”

为什么在演员表之后调用Circle.Draw()而不是Shape.Draw()?这是什么原因?

5 个答案:

答案 0 :(得分:13)

Casting不会更改运行时对象类型以及每个实例具有的特定虚拟方法的实现。

请注意,以下2个案例中您的样本相同:

Shape shape = new Circle();
shape.Draw(); //OK; This prints: Circle.Draw()

Circle circle = new Circle();
Shape shape = circle as Shape;
shape.Draw();

第一个基本上是第二个的缩短版本。

答案 1 :(得分:8)

正如其他人所提到的,投射物体并不会改变实际的实例;相反,cast允许变量从对象层次结构中的较高位置开始假设实例的特征子集。

要说明为什么需要以这种方式工作,请考虑以下示例:

//Some buffer that holds all the shapes that we will draw onscreen
List<Shape> shapesOnScreen = new List<Shape>();

shapesOnScreen.Add(new Square());
shapesOnScreen.Add(new Circle());

//Draw all shapes
foreach(Shape shape in shapesOnScreen)
{
    shape.Draw();
}

在foreach循环中调用Draw()将调用派生实例的Draw()方法,即Square.Draw()和Circle.Draw()。在此示例中,这允许您绘制每个单独的形状,而无需确切知道您在运行时绘制的形状。你只知道你需要一个形状,让形状处理它的绘制方式。

如果不是这种情况(这适用于其他语言的继承,而不仅仅是C#),那么除了Shape.Draw()之外,你将无法使用任何东西。

答案 2 :(得分:6)

您正在覆盖继承类中的方法,因此无论您是否引用不太具体的基类,它都将始终是被调用的版本。

如果要调用Shape中的版本,则需要类型为Shape的实例,而不仅仅是该类型的引用。

答案 3 :(得分:3)

其他答案绝对正确,但要尝试深入一级:

通过使用称为虚函数指针表(vTable)的东西来实现多态性。从本质上讲,你会得到类似的东西:

  

形状 - &gt; Shape.Draw()

     

圈子 - &gt; Circle.Draw()

当您拨打标记为&#34;虚拟&#34;的功能时编译器执行typeof,并调用该函数的最多派生实现,该实现是类型继承树的一部分。由于Circle继承自Shape,并且您有一个Circle对象(如前所述,转换不会影响基础类型),因此调用Circle.Draw。

显然,这是对实际情况的过度简化,但希望它有助于解释为什么多态行为的行为方式。

答案 4 :(得分:0)

让我用is-a来解释它,因为形状is是一个圆圈:

Shape shape = circle as Shape;

代码完美地解释了自己,你指的是圆圈as一个形状,形状根本没有变化,而is仍然是一个圆圈,尽管它is也是一个圆形形状

您甚至可以检查is是否为圆圈:

if (shape is Circle)
    Console.WriteLine("The shape is a Circle!");

is一个圆圈,对吗?因此,调用Circle.Draw()应该是完全符合逻辑的。