Hello, C#—1.14.2 Unboxing Leaves Us Downcast
1.14.2 Unboxing Leaves Us Downcast
There is not much we can do with an
object except invoke one of its public member functions. We cannot access any of the methods or properties of the original type. For example, when we assign a
string object to an
string s = "cat"; object o = s; // error: string property Length is not available // through the object instance ... if ( o.Length != 3 )
all knowledge of its original type is unavailable to the compiler. If we wish to make use of the
Length property, we must first return the
object back to a
string. However, an object is not automatically converted to another type:
// error: no implicit conversion of an object type // to any other type ... string str = o;
A conversion is carried out automatically only if it can be guaranteed to be safe. For the compiler to determine that, it must know both the source and the target types. With an
object instance, all type information is absent—at least for the compiler. (The type and environment information, however, is available both to the runtime environment and to us, the programmers, during program execution. We look at accessing that information in Chapter 8.)
For any conversion for which the compiler cannot guarantee safety, the user is required to do an explicit type cast—for example,
string str = ( string ) o;
The explicit cast directs the compiler to perform the type conversion even though a compile-time analysis suggests that it is potentially unsafe. What if the programmer is wrong? Does this mean we have a hard bug to dig out?
The full type information is available to the runtime environment, and if it turns out that
o really does not represent a
string object, the type mismatch is recognized and a runtime exception is thrown. So if the programmer is incorrect with an explicit cast, we have a bug, but because of the automatic runtime check, not one that is difficult to track down.
Two operators can help us determine the correctness of our cast:
as. We use the
is operator to ask if a reference type is actually a particular type—for example,
string str; if ( o is string ) str = ( string ) o;
is operator is evaluated at runtime and returns true if the actual object is of the particular type. This does not relieve us of the need for the explicit cast, however. The compiler does not evaluate our program's logic.
Alternatively, we can use the
as operator to perform the cast at runtime if the actual object is of the particular type that interests us—for example,
string str = o as string;
o is not of the appropriate type, the conversion is not applied and
str is set to
null. To discover whether the downcast has been carried out, we test the target of the conversion:
if ( str != null ) // OK: o does reference a string ...
In converting an
object instance to a particular reference type, the only work required is setting the handle to the
object's heap address. Converting an
object instance to a particular value type requires a bit more work because an object of a value type directly contains its data.
This additional work in converting a reference type back to a value type is called unboxing. The data copied into the previously generated box is copied back into the object of the target value type. The reference count of the associated box on the managed heap is decremented by 1.