Conversion Between Types in C#

Introduction

As we know C# is a type-safe language that I have discussed in my previous article. Type safety can be decided by the compiler at compile time or at run time by the CLR. I will explain here both of these types of type safety with some more examples and a small exercise in the end.

At runtime the CLR knows the type of an object. We can always discover the type of the object by calling the GetType() method. Since this method is non-virtual it is impossible for the type to spoof another type. Suppose we have an employee class as shown below.

  1. //All types is already derived from System.Object  
  2. public class Employee  
  3. {  
  4. }  
  5. public class Manager:Employee  
  6. {  
  7. }  
  8. public class SomeOtherType  
  9. {  
  10. }   
Compile time type Checking

The preceding Manager class cannot override GetType() and return CEO as the class. There are basically two types of conversions that the compiler and CLR take care of in C#. They are implicit and explicit conversion. Implicit conversion takes place without any chances of data loss and that is why they are called safe explicit conversion.

Since I have defined a class Employee that implicitly derives from System.Object it is absolutely safe to create a variable of type object and assign it a reference of type Employee as shown below. In a more generalized term we can say that we can always have a variable of base type that can contain a reference of the derived type.

 

  1. static void Main(string[] args)   
  2. {  
  3.    Object obj = new Employee(); // This is absolutely safe and compiler allows this  
  4.    Employee emp = (Employee)obj; //This is also safe as compiler knows at the compile time that the obj contains a reference of type Empl   oyee  
  5.    SomeOtherType otherType = (SomeOtherType)emp; //This is a compile type error for the same reason as compiler knows that obj is not a r   eference of SomeOtherType type  
  6. }  

Run time type Checking

There can be a scenario in which the compiler doesn't know the type of the object that it is casting to at compile time. The example of that is explained here.

I have a function defined as in the following.

 

  1. private static void ChangeDesignation(object o)  
  2. {  
  3.    Employee emp = (Employee)o; // At compile time the compile is not sure about the type of object o refers to that allows the code to    build, but at run time  
  4.    //the CLR checks that o should be Employee type or type derived from it, if not it throws InvalidCastException  
  5. }  

At compile time the compiler knows that o is the type of object and we are trying to explicitly cast o to Employee. That is fine since Employee derives from Object. Now if I want to pass an instance of type Manager in the same function, as shown below, the execution would work fine.

 

  1. static void Main(string[] args)  
  2. {  
  3.    Manager manager = new Manager();  
  4.    ChangeDesignation(manager); // This would work fine and it will create no problem at run time and in the function we can see that the    o is casted to the most base type only which is also safe  
  5.    SomeOtherType otherType = new SomeOtherType();  
  6.    ChangeDesignation(otherType);// The part of code will throw InvalidCastException at run time after it enters into the function  
  7. }  
Now in the next part of the code if I create an instance of SomeOtherType and pass as argument to the ChangeDesignation function the compiler would allow the code to compile but at run time we will get an InvalidCastException stating 'Unable to cast object of type 'TypeSafety.SomeOtherType' to type 'TypeSafety.Employee'.' since CLR knows that the SomeOtherType is not derived from the Employee class. This part of code was to show the Run Time Type checking. If the CLR would have allowed the cast, there would not have been any type safety and the result would have been unpredictable, including an application crash and security breaches caused by the ability of types to easily spoof other types.

An easy solution to prevent this type of run time exception would have been to declare ChangeDesignation with Employee type as parameter type instead of object type so that the compiler produces a compile-time error. An object as a parameter type has been used here in this example to show the run time type checking.

Casting with the C# is and as Operators

Apart from explicit casting that we have used in the ChangeDesignation method, is to check the valid casting using the is operator. The is operator checks whether an object is compatible with a given type and the result of the evaluation is a Boolean, whether true or false. The is operator never throws an exception. Kindly check the following code:

 

  1. Object o = new Object();  
  2. Boolean b1 = (o is object);//true  
  3. Boolean b2 = (o is Employee);//false  

If the object reference is null, the is operator always returns false since there is no object to check its type. The is operator could have been typically used as in the following in the ChangeDesignation function:

 

  1. if(o is Employee)  
  2.    Employee emp = (Employee)o;  

The CLR's type checking improves security, but it certainly comes at a performance cost, because the CLR must determine the actual type of the object referred to by the variable (o) and then the CLR must walk the inheritance hierarchy, checking each base type against the specified type (Employee). Since we need this kind of programming paradigm quite often, C# offers the as operator that simplifies our task and improves the performance bottleneck that is shown in the following code snippet:

 

  1. Employee emp = o as Employee;  
  2. if(emp != null)  
  3. {  
  4.    //use the emp variable  
  5. }  

The as operator in the preceding code checks if the o is compatible with the Employee type and if it is, as returns a non-null reference to the same object. if o is not compatible, it simply returns null without throwing any exception. If we use the preceding code without checking for null, it can be dangerous as shown below:

 

  1. Object o = new Object();  
  2. Employee e = o as Employee; //casts o to Employee which returns null  
  3. e.ToString();// throws exception.  

Now I would like to discuss a very interesting exercise that we would perform here and see if the code is fine or we will get a Run Time Error (RTE) or Compile Time Error (CTE) using the two classes shown below.

 

  1. public class Base //base class  
  2. {  
  3. }  
  4. public class Derived: Base //derived class  
  5. {  
  6. }  
  7. static void Main(string[] args)  
  8. {  
  9.     Object o1 = new Object(); //Works fine  
  10.     Object o2 = new Base(); //Works fine  
  11.     Object o3 = new Derived(); //Works fine  
  12.     Object o4 = o3; //Works fine  
  13.     Base b1 = new Base(); //Works fine  
  14.     Base b2 = new Derived(); //Works fine  
  15.     Derived d1 = new Derived(); //Works fine  
  16.     Base b3 = new Object(); //CTE as the b3 is a varible of Base type which derives from Object. We can have a instance of base type and reference of derived type  
  17.     Derived d2 = new object(); //CTE for the same reason as above  
  18.     Base b4 = d1; //Works fine  
  19.     // Derived d3 = b2;// CTE we cannot simply convert a base class instance to derived type. we need to tell the compiler to explicitly convert to derviced type  
  20.     //object. it should be Derived d3 = (Derived)b2  
  21.     Derived d4 = (Derived) d1; //Works fine  
  22.     Derived d5 = (Derived) b2; //Works fine  
  23.     Derived d6 = (Derived) b1; //CTE for the reason that at compile time the compile is fine to cast the variable to base type, but at runtime the CLR checks that the b1 is an instance of type Base which contains a reference to Base type only and not the derived type  
  24.     Base b5 = (Base) o1; //CTE for the same reason as above  
  25.     Base b6 = (Derived) b2; //Works Fine  
  26. }