多态性和铸造

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

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

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

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

和:

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

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

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

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

 //Some buffer that holds all the shapes that we will draw onscreen List shapesOnScreen = new List(); 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()。 在此示例中,这允许您绘制每个单独的形状,而无需确切知道在运行时绘制的形状。 你只知道你需要一个形状,让形状处理它的绘制方式。

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

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

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

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

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

形状 – > Shape.Draw()

圆圈 – > Circle.Draw()

当您调用标记为“virtual”的函数时,编译器会执行typeof,并调用该函数的派生程序最多的实现,该函数属于类型inheritance树的一部分。 由于Circleinheritance自Shape,并且您有一个Circle对象(如前所述,转换不会影响基础类型),因此调用Circle.Draw。

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

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

 Shape shape = circle as Shape; 

代码完美地解释了自己,你将圆圈as一个形状,形状根本没有变化,它仍然是一个圆形,虽然它也是一个形状。

你甚至可以检查它is一个圆圈:

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

is一个圆圈吧? 所以调用Circle.Draw()应该是完全合乎逻辑的。