user3105717
user3105717

Reputation: 121

Polymorphism and casting

I want to understand polymorphism in c# so by trying out several constructs I came up with the following case:

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

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

I understand that in order to send the Draw() message to several related objects, so they can act according to its own implementation I must change the instance to which (in this case) shape is 'pointing' to:

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

But why, when I do this:

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

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

It prints: "Circle.Draw()"

Why it calls the Circle.Draw() instead Shape.Draw() after the cast? What is the reasoning for this?

Upvotes: 12

Views: 2982

Answers (5)

Ken Hung
Ken Hung

Reputation: 782

Let me explain it in terms of is-a, because the shape is a circle:

Shape shape = circle as Shape;

The code explains itself perfectly, you are referring the circle as a shape, the shape does not change at all, and it is still a circle, although it is also a shape.

You can even check if it is a circle:

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

It is a circle, right? So invoking Circle.Draw() should be perfectly logical.

Upvotes: 0

Nishmaster
Nishmaster

Reputation: 525

As others have mentioned, casting an object doesn't change the actual instance; instead, casting allows a variable to assume a subset of characteristics of the instance from higher in the object hierarchy.

To demonstrate why it needs to work this way, consider this example:

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

Calling Draw() in the foreach loop will call the Draw() method of the derived instance, i.e. Square.Draw() and Circle.Draw(). This allows you, in this example, to draw each individual shape without knowing exactly which shape you're drawing at runtime. You just know you need a shape, let the shape handle how it's drawn.

If this was not the case (and this goes for inheritance in other languages, not just C#), you wouldn't be able to use anything but Shape.Draw().

Upvotes: 8

BradleyDotNET
BradleyDotNET

Reputation: 61379

The other answers are absolutely correct, but to try and go one level deeper:

Polymorphism is implemented by using something called a virtual function pointer table (vTable). In essence, you get something like:

Shape -> Shape.Draw()

Circle -> Circle.Draw()

When you call a function marked "virtual" the compiler does a typeof, and calls the most derived implementation of that function that is part of the types inheritance tree. Since Circle inherits from Shape, and you have a Circle object (as noted before, casting does not affect the underlying type), Circle.Draw is called.

Obviously this is an oversimplification of what actually happens, but hopefully it helps explain why polymorphic behavior acts the way it does.

Upvotes: 3

Alexei Levenkov
Alexei Levenkov

Reputation: 100630

Casting does not change run-time type of object and what implementation of particular virtual method each instance have.

Note that following 2 cases you have as sample are identical:

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

and:

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

The first one is essentially shorter version of the second.

Upvotes: 13

evanmcdonnal
evanmcdonnal

Reputation: 48154

You're overriding the method in the inheriting class so it will always be the version that gets invoked regardless of whether your reference is to the less specific base class.

If you want to invoke the version in Shape you need an instance of type Shape not just a reference of that type.

Upvotes: 6

Related Questions