C Sharp

Casting Between Types

At this point, let's look at one of the most important aspects of types: casting. Assuming a base class named Employee and a derived class named ContractEmployee, the following code works because there's always an implied upcast from a derived class to its base class: -

class Employee { }
class ContractEmployee : Employee { }
class CastExample1
{
    public static void Main ()
    {
        Employee e = new ContractEmployee();
    }
}

However, the following is illegal, because the compiler cannot provide an implicit downcast: -

class Employee { }
class ContractEmployee : Employee { }
class CastExample2
{
    public static void Main ()
    {
        ContractEmployee ce = new Employee(); // Won't compile.
    }
}

The reason for the different behavior goes back to the Chapter 1, "Fundamentals of Object-Oriented Programming," and the concept of substitutability. Remember that the rules of substitutability state that a derived class can be used in place of its base class. Therefore, an object of type ContractEmployee should always be able to be used in place of or as an Employee object. That's why the first example compiles.

However, you cannot cast an object of type Employee down to an object of type ContractEmployee because there's no guarantee that the object supports the interface defined by the ContractEmployee class. Therefore, in the case of a downcast, an explicit cast is used as follows: -

class Employee { }
class ContractEmployee : Employee { }
class CastExample3
{
    public static void Main ()
    {
        //Downcast will fail.
        ContractEmployee ce = (ContractEmployee)new Employee(); 
    }
}

But what happens if we lie and try to trick the CTS by explicitly casting a base class to a derived class as follows? -

class Employee { }
class ContractEmployee : Employee { }
class CastExample4
{
    public static void Main ()
    {
        Employee e = new Employee();
        ContractEmployee c = (ContractEmployee)e;
    }
}

The program compiles, but running the program generates a run-time exception. There are two things to note here. First, the result is not a compile-time error because e might have really been an upcasted ContractEmployee object. Therefore, the true nature of the upcasted object can't be known until run time. Second, the CLR determines the object types at run time. When the CLR recognizes an invalid cast, it throws a System.InvalidCastException.

There is one other way of casting objects: using the as keyword. The advantage to using this keyword instead of a cast is that if the cast is invalid, you don't have to worry about an exception being thrown. What will happen instead is that the result will be null. Here's an example: -

using System;
class Employee { }
class ContractEmployee : Employee { }
class CastExample5
{
    public static void Main ()
    {
        Employee e = new Employee();
        Console.WriteLine("e = {0}",
                       e == null ? "null" : e.ToString());
        ContractEmployee c  = e as ContractEmployee;
        Console.WriteLine("c = {0}",
                           c == null ? "null" : c.ToString());
    }
}

If you run this example, you'll see the following result: -

c:>CastExample5
e = Employee
c = null

Note that the ability to compare an object to null means that you don't have to run the risk of using a null object. In fact, if the example had attempted to call a System.Object method on the c object, the CTS would have thrown a System.NullReferenceException.