Because a variable of type object can refer to a boxed copy of a value, it's only reasonable to allow you to get at that boxed value through the variable. You might expect to be able to access the boxed int value that a variable o refers to by using a simple assignment statement such as:
int i = o;
However, if you try this syntax, you'll get a compile time error. If you think about it, it's pretty sensible that you can't use the int i = o; syntax. After all, o could be referencing absolutely anything and not just an int. Consider what would happen in the following code if this statement was allowed:
Circle c = new Circle(); int i = 42; object o; o = c; // o refers to a circle i = o; // what is stored in i?
To obtain the value of the boxed copy, you must use what is known as a cast, an operation that checks that it is safe to convert one type to another, and then does the conversion. You prefix the object variable with the name of the type, in parentheses, as in this example:
int i = 42; object o = i; // boxes i = (int)o; // compiles okay
The effect of this cast is subtle. The compiler notices that you've specified the type int in the cast. Next, the compiler generates code to check what o actually refers to, at runtime. It could be absolutely anything. Just because your cast says o refers to an int, that doesn't mean it actually does. If o really does refer to a boxed int and everything matches, the cast succeeds and the compiler-generated code extracts the value from the boxed int. (In this example, the boxed value is then used to initialize i.) This is called unboxing. The following diagram shows what is happening:
However, if o does not refer to a boxed int there is a type mismatch, causing the cast to fail. The compiler-generated code throws an InvalidCastException at runtime. Here's an example of an unboxing cast that fails:
Circle c = new Circle(42); object o = c; // doesn't box because Circle is a class int i = (int)o; // compiles okay, but throws at runtime
You will use boxing and unboxing in later exercises. Keep in mind that boxing and unboxing are expensive operations because of the amount of checking required, and the need to allocate additional heap memory. Boxing has its uses, but injudicious use can severely impair the performance of a program. You will see an alternative to boxing in Chapter 17, “Introducing Generics.”
In this chapter, you have learned some important differences between value types that hold their value directly on the stack and reference types that refer indirectly to their objects on the heap. You have also learned how to use the ref and out keywords on method parameters to gain access to the arguments. You have seen how assigning a variable of the System.Object class to a value (such as 42) causes the variable to refer to a boxed copy of the value made on the heap. You have seen how assigning a variable of a value type to a variable of the System.Object class causes the boxed copy to refer to a variable of a value type (such as int).
If you want to continue to the next chapter
Keep Visual Studio 2005 running and turn to Chapter 9.
If you want to exit Visual Studio 2005 now
On the File menu, click Exit. If you see a Save dialog box, click Yes.