我想了解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()?这是什么原因?
答案 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()
应该是完全符合逻辑的。