Inheritance and Polymorphism in C#

368 views Asked by At

There's a simple question, What will the program print to the console?

I never thought that I could be wrong as much as I was with this piece of code. It behaved the other way around of what my logic expected from it.

If someone could please enlighten the reason for each line printed I would be really appreciate that.

And also, what is the meaning of instantiating a new Circle and casting it to a Shape? why is it treated also as an Ellipse?

Thank you so much

EDIT: I was asked to specify what was I expecting the output to be. So:

  1. creating a Circle and casting it to a Shape, I was thinking that only the Shape C'tor will be executed, as it's a Shape.

  2. What's the point of calling super() if it's done automatically? the Ellipse C'tor executed the code in the Shape C'tor.

  3. Why does x1.Draw() which is casted to Shape is executing the code of Ellipse's Draw()? as you can see both x1,x2 printed the same message.

Hope I was more clear, thank you.

namespace ConsoleApplication2
{
    class Shape
    {
        public Shape()
        { 
            Console.WriteLine("SHAPE CTOR");
        }

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

    class Ellipse : Shape
    {
        public Ellipse()
        {
            Console.WriteLine("ELLIPSE CTOR");
        }

        public sealed override void Draw()
        {
            Console.WriteLine("ELLIPSE.Draw()");
        }
    }

    class Circle : Ellipse
    {
        public static void Main()
        {
            Shape x1 = (Shape)new Circle();
            Ellipse x2 = (Ellipse)new Circle();
            Circle x3 = new Circle();
            x1.Draw();
            x2.Draw();
            x3.Draw();
        }

        public void Draw()
        {
            Console.WriteLine("CIRCLE DRAW");
        }
    }
}

Output:

SHAPE CTOR
ELLIPSE CTOR
SHAPE CTOR
ELLIPSE CTOR
SHAPE CTOR
ELLIPSE CTOR
ELLIPSE.Draw()
ELLIPSE.Draw()
CIRCLE DRAW
3

There are 3 answers

0
Thomas Levesque On BEST ANSWER

creating a Circle and casting it to a Shape, I was thinking that only the Shape C'tor will be executed, as it's a Shape.

When you write new Circle(), it creates an instance of Circle, so it calls the constructor of Circle. The cast is done afterwards. Actually, in this case the cast is redundant, because a Circle already is a Shape. You're just assigning the instance of Circle to a variable of type Shape; this way the compiler doesn't know the actual concrete type of the variable. The Circle instance is not actually converted to anything else.

What's the point of calling super() if it's done automatically? the Ellipse C'tor executed the code in the Shape C'tor.

I assume you mean base() rather than super(); but anyway, you're not calling it, the compiler does it for you automatically. The constructor of a derived class must always call a constructor of the base class to initialize the state of the base class. In the case of the default constructor, you don't need to do it explicitly because the compiler does it for you.

Why does x1.Draw() which is casted to Shape is executing the code of Ellipse's Draw()? as you can see both x1,x2 printed the same message.

That's the whole point of polymorphism. At runtime, the method that is executed depends on the actual concrete type of the object. Since Ellipse overrides the Shape.Draw method, it is the Ellipse implementation that is executed for an instance of Ellipse.

Note that in the Circle class, you didn't use the override keyword on the Draw method; this means that Circle.Draw does not override Shape.Draw, it's just an unrelated method that just happens to have the same name. It will not participate in polymorphism, it will only be called if you call it through a variable of type Circle.

1
a-ctor On
  1. creating a Circle and casting it to a Shape, I was thinking that only the Shape C'tor will be executed, as it's a Shape.

    As you are saying: You are creating a Circle and casting it to a Shape. The constructor will only be called when you are creating an instance of an object. Therefor you are simply calling the constructor of Circle. The process of casting is not calling any constructors.

  2. You always need to call the base constructor with : base(). When there is a parameter less base constructor the compiler will add the : base() for you (Because its clear). You can use base() to call a specific base constructor which might not be the parameterless one.

  3. The Draw() method you have there in your Circle class is only known by the compiler when you have a Circle instance. In you case you have got a Ellipse and a Shape which don't know about the custom Drawmethod and are therefor calling the overridden Draw method from Ellipse

0
Patrick Hofman On
  1. For every type in the inheritance tree (at least) one constructor must be called. Hence you see every constructor print in your case.
  2. To redirect to a specific (overload of an) constructor.
  3. Since the last implementation on the class is called. Even if the object is casted, it will still call the last type's method (unless you break inheritance through new).