|Casting – the escape from strong typing|
Page 3 of 5
If there is an upcast you can guess that there is going to be a downcast. In fact downcasting is more commonly used than upcasting because it allows you to reference all of the derived classes, their methods and properties using the base class type.
In many ways this is a more mysterious process and more difficult to get used to because downcasting moves down the class hierarchy to objects that are “bigger” than the base class. This is also potentially unsafe because you might try to use a method or property that the “bigger” class doesn’t actually support. For this reason downcasting is always explicit.
Downcasting, moving down the inheritance hierarchy, isn’t safe
Consider the following example. First we create a MyA that references a ClassB object:
Notice that this is an example of upcasting and to a certain extent this upcasting only to downcast again is slightly artificial but it isn’t difficult to find a more realistic example once the idea has been explained. Next we downcast the MyA reference to a MyB object:
MyB = (ClassB)MyA;
In this case the cast has to be explicit and following this you can now call ClassB methods as in:
This example is easy to follow but it doesn’t really give you any hint of why you might want to downcast – after all you could have used the ClassB object more sensibly by referencing it in using a ClassB type in the first place.
What downcasting allows you to do is to use a reference to a class without really knowing what it is. The most extreme example of downcasting is to use an object reference for every class in your program. For example:
Why might this be useful?
The answer is that you can now write code that will work with any type using nothing but object references. Of course the catch is that you need to know how to downcast the reference correctly so that you can use the members that are specific to the class and there is always the very real chance that you will get it all wrong.
For example, if we change MyAnything to a ClassA object and try to downcast it to a ClassB object:
MyAnything = new ClassA();
then you don’t get a compile time error but you do get a run time exception.
If you abandon strong typing by using casts then this is the sort of problem you have to live with. There are two operators that can help avoid runtime exceptions. The is operator can be used to test the type of the reference before applying a cast. For example:
if (MyAnything is ClassB)
The as operator is an alternative way of writing a cast but with the difference that if it fails the reference is to null. For example:
ClassB MyB = MyAnything as ClassB;
never throws an exception but you have to make sure that you don’t end up using a null reference because it didn’t actually work.
Downcasting can be dangerous if the object isn’t the type you think it is