2

I have been recently studying upcasting and downcasting in c#. I understand that upcasting refers to conversion from a derived class to a base class. However, when i see a practical example of upcasting (like below), i get confused.

public class Shape 
{
...
}

public class Circle : Shape
{
...
}

Circle circle = new Circle();
Shape shape = new Shape();
// Converting an object into its base class reference
shape = circle

If we are converting circle into its base class reference, shouldn't it be like

circle = shape 

Sorry if it sounds too amateur. This is because I have always seen expressions in the following format:

int x = 3; // means assign 3 to variable x. 

So I am just confused why circle is on the right hand side and not on the left hand side. Please advise. Consider me a beginner.

1

6 Answers 6

5

(Aside: This is called upcasting because, traditionally, class diagrams are drawn such that the base classes are shown physically above the derived classes.)

Now when you do:

shape = circle; // shape->(instance of Circle)

you are assigning a Circle reference to a Shape reference, so that after the assignment the reference shape will be referencing a Circle.

This is fine, because everything you can do with a Shape you can also do with a Circle.

If, however, you do:

circle = shape; // circle->(instance of Shape)

you are assigning a Shape reference to a Circle reference. You can't do this because (if this were possible) you would then be able to access Circle features that are not present in a Shape.

For example, imagine that Circle.Radius exists but Shape.Radius does not.

If you were allowed to point the circle reference at a Shape what would happen if you tried to access circle.Radius? The answer would be: undefined behaviour would happen, because Shape.Radius does not exist.

2

No

The problem is: a Shape is not a Circle, but a Circle is a Shape.

This means that a Circle always can be placed into a spot where a Shape is required, but when you need a Circle you could not throw into its place any Shape as it may also be a Triangle.

This is why you need to explicitly cast it up (either using (Type)variable or variable as Type).

Essentially, lets assume the following:

class Shape {}
class Circle : Shape {}
class Triangle : Shape {}

Now, we also have a Triangle allowing us to actually get this layed out better:

Circle c = new Circle();
Triangle t = c; // Impossible due to Circle not being a Triangle
Shape s = c; // Possible because a Circle is a Shape
Triangle t2 = s; // Impossible because Shape may be a Triangle or any other derived class
1

What you are basically doing in your example can also be written as the following:

Circle circle = new Circle();
Shape shape = (Shape)circle; //Casting the object to the base type

So you are casting your object of type Circle to an object of type Shape. In your code this is "automatically" done because you are assining the value to a new variable of type Shape.

This explains the casting/base classes a little bit more: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions

1

if you know about set, you can easily understand why 'shape = circle' is able, 'circle = shape' is not able.

think about this.

A is a character of Alphabet.

so we can say

public class A : Alphabet
{
}

as we know.

also Alphabet is

public class Alphabet
{
}

if we draw a diagram, it can be like this

┌───────── Alphabet ───────┐
│ A B C D E ... a b c ... z│
└──────────────────────────┘

We can say z is Alphabet, but we cannot say Alplabet is z because Alphabet contains more than z.

so, Let's see Circle and Shape.

Circle contains information of Shape and it has been added more variables. (possibly it does not, but can be changed. )

So we can say Circle is a set of Shape.

We can modify Circle which is based on Shape. Also we can Initialize Circle.

but if you do 'Shape = Circle', some of things which is in Circle cannot be initialized.

and it returns error.

1

I'll try to explain:

The variable shape contains a reference to an object of type Shape.

When you write Shape shape = new Shape(); The variable shape will contain a reference to a new Shape object.

When you reassign shape by writing shape = circle it contains a reference to another object. The object circle is of type circle, but since circle inherits shape it is OK to do this assignment; there is an implicit cast done of the circle to type Shape.

Another and perhaps clearer way to do this cast would be to make an explicit cast:

Circle circle = new Circle();
Shape circleAsShape = (Shape)circle;
1

Upcasting means changing type of an object to a less derived base class (Circle object to Sape).

Downcasting works in the other direction, eg. casting from Shape to Circle.

Variable of more derived type can be easily assigned to variable of less derived type, because upcasting is done implicitly here, because Circle is also a Shape. That's why value of more derived type can be assigned to a vairable of less derived type.

It doesn't work the other way, since Shape is general and we don't know if it's a Circle. Thus, when you try downcasting, you need to specify it explicitly:

// this won't work
Shape shape = new Shape();
Circle c = (Circle)shape;
// this will
Shape shape = new Circle();
// we know we have Circle object so we can downcast
Circle c = (Circle)shape;

Not the answer you're looking for? Browse other questions tagged or ask your own question.