CHAPTER 6 UNDERSTANDING INHERITANCE AND POLYMORPHISM
The previous code compiles given the implicit cast from the base class type (Employee) to the derived
type. However, what if you also wanted to fire Frank Zappa (currently stored in a general System.Object
reference)? If you pass the frank object directly into this method, you will find a compiler error as
follows:
// Error!
object frank = new Manager("Frank Zappa", 9, 3000, 40000, "111-11-1111", 5);
GivePromotion(frank);
The problem is that you are attempting to pass in a variable which IS-NOT-A Employee, but a more
general System.Object. Given the fact that object is higher up the inheritance chain than Employee, the
compiler will not allow for an implicit cast, in an effort to keep your code as type-safe as possible.
Even though you can figure out that the object reference is pointing to an Employee-compatible
class in memory, the compiler cannot, as that will not be known until runtime. You can satisfy the
compiler by performing an explicit cast. This is the second law of casting: you can, in such cases,
explicitly downcast using the C# casting operator. The basic template to follow when performing an
explicit cast looks something like the following:
(ClassIWantToCastTo)referenceIHave
Thus, to pass the object variable into the GivePromotion() method, you must author the following
code:
// OK!
GivePromotion((Manager)frank);
The C# as Keyword
Be very aware that explicit casting is evaluated at runtime, not compile time. Therefore, if you were to
author the following C# code:
// Ack! You can't cast frank to a Hexagon, but this compiles fine!
Hexagon hex = (Hexagon)frank;
you would compile without error, but would receive a runtime error, or more formally a runtime
exception. Chapter 7 will examine the full details of structured exception handling; however, it is worth
pointing out, for the time being, that when you are performing an explicit cast, you can trap the
possibility of an invalid cast using the try and catch keywords (again, see Chapter 7 for full details):
// Catch a possible invalid cast.
try
{
Hexagon hex = (Hexagon)frank;
}
catch (InvalidCastException ex)
{
Console.WriteLine(ex.Message);
}
While this is a fine example of defensive programming, C# provides the as keyword to quickly
determine at runtime whether a given type is compatible with another. When you use the as keyword,
you are able to determine compatibility by checking against a null return value. Consider the following:
242