C# Upcasting and Downcasting


This entry is part 5 of 10 in the series C# Inheritance

The post C# Introduction lists all of the C# series of posts we have at this site.

What is upcasting and downcasting? Upcasting is conversion from a derived (child) class to a base (parent) class. It’s like going up the family tree. Downcasting is the opposite: going from a base class to a derived class, down the tree. We will also look at the as and is keywords.

Upcasting

Let’s say we have a class called Shape and a class called Circle. Shape is the parent and circle is the child. Below is some code you could try for yourself. An object reference can be implicitly converted to a base class reference. Below we convert a circle reference to a shape reference (base class/parent class).

            Circle circle = new Circle();
            Shape shape = circle;  // upcasting

So here is a bit more of a real-world example from Mosh Hamedani’s course at Udemy.com.

namespace UpcastingDowncasting
{
    public class Shape
    {
        public int Width { get; set; }
        public int Height { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public void Draw(){ }
    }
    public class Text : Shape
    {
        public int FontSize { get; set; }
        public string FontName { get; set; }
    }
    public class Circle : Shape
    {
    }
    class Program
    {
        static void Main(string[] args)
        {
            Text text = new Text();
            Shape shape = text;  // upcasting - implicit conversion here
            // text and shape are both references to the same object in memory.
            // shape does not have access to FontSize or FontName, soo
            // what's the point of limiting yourself?
        }
    }
}

Both text and sharp in the above code are referencing the same object but they are not the same. They have different views of that object. Both can access and modify the Height and Width for example. Now here we are starting to hint at polymorphism. So the question is, when would I need to know how to do this? What’s a real-world example?

StreamReader

The StreamReader works with streams. A stream could be a file or a memory stream. If we have an object of type SteamReader we can see how this works. The Object Browser shows you that FileStream derives from Stream. MemoryStream derives from Stream. In the constructor of the StreamReader object we can pass any object who’s type derives from the Stream class. Therefore we can pass a new FileStream or a new MemoryStream. Let’s look at some code.

StreamReader reader = new StreamReader(new MemoryStream());

MemoryStream will be automatically upcast to Stream in the example above.

ArrayList

Consider another example. Normally we don’t use ArrayList as it is prone to errors made by programmers. A better way is to use a Generic list because with that we specify the type of objects that are inside the list. All of the objects in the Generic list will all be of the same type.

            ArrayList aryList = new ArrayList();
            aryList.Add(1);
            aryList.Add("Mosh");
            aryList.Add(new Circle());
        
            var anotherList = new List<int>();

So what is the key take-away from this? In C# upcasting is implicit so we can convert an object’s reference to its base class reference. Wherever a method requires an object of a given type, you can pass an object of that type or of any of the types that derive from that type.

Downcasting

Here is an example of downcasting. We have a shape called shape and we are going to assign it to a Text. At compile time it is a shape but at run time it will be Text. How can this be? Press F9 to place a break point. Press F5 to enter debug mode and run it to the break point which you placed at Shape shape = new Text();. Next, press F10.

            Shape shape = new Text();
            Text text = (Text) shape;  // downcasting!
            Console.WriteLine(text.FontSize.ToString("F5"));
            //  0.0000

The second line in the above code is an example of downcasting. We are using a cast. Below is a screenshot of what it looks like in Visual Studio in debug mode using a Watch Window and a breakpoint. Note that when we downcast we have full access to the properties and methods of the Text class. We had access to FontSize.

Downcasting in WPF

A real-world example of downcasting can be found in a Windows Presentation Foundation project. Create a new project.

In the Toolbox on the left side of the IDE, under Common WPF Controls, find the Button. Drag it into the window. Double-click the button. Now you can type code. Just to test things out we can display a message box when the user clicks the button.

The properties of sender do not include anything about the button class, as you can see from the screenshot above. So the code below is what we have after we typed in our message box and the rest of the solution.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var button = (Button) sender;
            // button has access to properties and methods of button class.
            // use the as keyword when you are not sure of runtime type
            var button2 = sender as Button;
            if (button2 != null)
            {  // now we know conversion was successful
                MessageBox.Show("Height: " + button2.ActualHeight.ToString());
            }
            MessageBox.Show("Hello World!");
        }

So you can use an explicit cast or the as keyword. You have two options. The code above shows both options. Use an explicit cast when you are sure of the type and the as keyword when there is doubt.

Series Navigation<< C# Constructors and InheritanceC# Boxing and Unboxing >>

Leave a Reply