多态性和铸造

本文关键字:多态性 | 更新日期: 2023-09-27 17:58:30

我想了解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()?原因是什么?

多态性和铸造

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

请注意,以下两种情况作为样本是相同的:

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

和:

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

第一种基本上是第二种的较短版本。

正如其他人所提到的,强制转换对象不会改变实际实例;相反,强制转换允许一个变量从对象层次结构中的更高层假设实例的特征子集。

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

//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(。你只知道你需要一个形状,让这个形状来处理它的绘制方式。

如果不是这样的话(这适用于其他语言中的继承,而不仅仅是C#),那么除了Shape.Draw().

之外,您将无法使用任何东西

您正在重写继承类中的方法,因此无论您的引用是否指向不太特定的基类,它都将始终是被调用的版本。

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

其他答案都是绝对正确的,但要尝试更深入一层:

多态性是通过使用一种叫做虚拟函数指针表(vTable)的东西来实现的。本质上,你会得到这样的东西:

形状->Shape.Draw()

圆圈->Circle.Draw()

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

显然,这是对实际发生的事情的过于简单化,但希望它能帮助解释多态行为的行为方式。

让我用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()应该是完全合乎逻辑的。