Upcasting and Downcasting

.Upcasting and Downcasting Objects


Upcasting and Downcasting Program Output

.Upcasting and Downcasting

"Upcasting and downcasting an object does not change its type, the object always remains the same type it was born. When you upcast the object (up the inheritance hierarchy), you can only use the methods and properties of the base class. You can then downcast the object to return its use of methods and properties that were defined in the derived class."

The example program illustrates the use of upcasting and downcasting. Upcasting and downcasting are an important part of object-oriented languages. This involves the ability to treat a derived class as if it were a member any base class, and then use the base classes methods and properties (you may recognize this as polymorphism).

Upcasting and downcasting are NOT like casting primitives. Upcasting is treating a derived object as if it belonged to a base class. This is performed implicitly and is always successful at run time because the compiler is able to check the compatibility between the two classes at compile-time.

Downcasting is returning the object back to its derived type behavior. This must be performed explicitly by the programmer and will cause an "InvalidCastException" if the two classes are found to be incompatible at run-time.

The example program has a base class which only has the ConsumePower() method defined. The derived class has a different implementation of the ConsumerPowe() method defined, plus an additional MakeToast() method. When an object is created as the derived class it can perform both methods as they are defined in the derived class (i.e. both ConsumePower() and MakeToast()). When the object is upcast to the base class, it can no longer perform the MakeToast() method as it was not defined in the base class. It also runs the different implementation of the ConsumePower() method that was defined in the base class. The object type has not changed, but upcasting restricted it to act as a base class. When the object is downcast back to its derived class, it can once again perform both methods as they are defined in the derived class. The following C# features are used in the example code:

  1. Polymorphism, Up-casting and Down-casting - C# Corner
  2. Casting – the escape from strong typing - I Programmer

Upcasting and Downcasting Objects

/**************************************************************************************
* Upcasting and Downcasting an object does NOT change the object's type              *
* (like casting does with primitives). Instead object casting creates                *
* reference variables which control the types of behaviors which can be performed    *
* against the object. In the example below a Toaster is created, but then it is      *
* upcast to an Appliance. The Toaster reference can make toast, while the Appliance  *
* reference can not ... even though both references are pointing to the same Toaster *
* object.  Downcasting the Appliance back to a Toaster provides another reference    *
* that can be used to call the object's MakeToast() method once again                *
**************************************************************************************/
namespace UpCastingAndDownCasting
{
    class Appliance { public void ConsumePower() { System.Console.WriteLine(" ConsumePower: Just the pilot light ... 1 W"); } }
    class Toaster : Appliance
    {
        protected int toasterNumber = 0;
        public Toaster(int toasterNumber=0) { this.toasterNumber = toasterNumber; }
        public new void ConsumePower() { System.Console.WriteLine(" ConsumePower: Heating up ... 1500 W"); }
        public void MakeToast(string referenceName, string toastType="Nothing" )       
        {       
            System.Console.WriteLine(" MakeToast: {0} is toasting {1} in toaster number {2}", referenceName, toastType, toasterNumber);
        }
    }
   
    class Program
    {
        static void Main()
        {
            System.Console.WriteLine("Upcasting/Downcast of Objects:");
            System.Console.WriteLine(" Same object, but permitted behaviors depend upon the behaviors");
            System.Console.WriteLine("assocated with the type of the reference variable.\n");
            // Create a Toaster and make toast.
            Toaster myToaster = new Toaster(1);
            System.Console.WriteLine("\n1. myToster is born a Toaster.");
            System.Console.WriteLine("Born: [Toaster myToaster = new Toaster(1);]");
            myToaster.MakeToast("myToaster", "Waffle");     // Valid - Toaster object can make toast
            myToaster.ConsumePower();

            /**********************************************************
             * Upcast reference myToaster to an Applicance.           *
             * Object is still a Toaster, but new reference to        *
             * it (myAppliance) will only perform Applicance methods. *
             **********************************************************/
            Appliance myAppliance = myToaster; // Upcast            
            // Using myApplicance Reference, Toaster can no longer make toast
            //myAppliance.MakeToast("myApplicane","Pop Tart"); - Not a valid method call           
            System.Console.WriteLine("\n2. Upcast Toaster to Appliance, can no longer make toast.");
            System.Console.WriteLine("Upcast: [Appliance myAppliance = myToaster;]");
            System.Console.WriteLine(" MakeToast is Invalid - Why can't I toast? I am the same Toast object.");
            myAppliance.ConsumePower();

            /***************************************************************
             * Downcast reference myAppliance back to a Toaster.           *
             * myAppliance and myToaster are the same Object, but new      *
             * Toast type reference (cathyToaster) allows it to make toast.*
             ***************************************************************/           
            Toaster cathyToaster = (Toaster)myAppliance; // Downcast
            System.Console.WriteLine("\n3. Downcast Appliance back to a Toaster, can once again make toast.");
            // Using new cathyToaster Reference, Toast can make toast again
            System.Console.WriteLine("Downcast: [Toaster cathyToaster = (Toaster)myAppliance;]");
            cathyToaster.MakeToast("cathyToaster","Bagel");
            cathyToaster.ConsumePower();
          
            // myToaster, myAppliance, and cathyToaster reference the same object,
            // they just permit different behaviors depending on which reference used.
            System.Console.WriteLine("\n--- Reference Equality Tests ---");
            System.Console.WriteLine("Is myToaster    the same object as myAppliance: {0}",
                                      myToaster.Equals(myAppliance)); // Prints true
            System.Console.WriteLine("Is myAppliance  the same object as cathyToaster: {0}",
                          myAppliance.Equals(cathyToaster)); // Prints true
            System.Console.WriteLine("Is cathyToaster the same object as myToaster: {0}",
                                      cathyToaster.Equals(myToaster)); // Prints true

            System.Console.WriteLine("\n--- Object Types Associated with References ---");
            System.Console.WriteLine("myToaster    is an {0}", myToaster.GetType()); // Prints: Toaster
            System.Console.WriteLine("myApplicance is an {0}", myAppliance.GetType());// Prints: Toaster
            System.Console.WriteLine("cathyToaster is an {0}\n", myAppliance.GetType());// Prints: Toaster
        }
    }
}