Understanding Generics—Revisiting Boxing and Unboxing
Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio
Revisiting the Boxing, Unboxing, and System.Object Relationship
To understand the benefits provided by generics, it is helpful to understand the “issues” programmers
had without them. As you recall from Chapter 3, the .NET platform supports automatic conversion
between stack-allocated and heap-allocated memory through boxing and unboxing. At first glance,
this may seem like a rather uneventful language feature that is more academic than practical. In
reality, the (un)boxing process is very helpful in that it allows us to assume everything can be treated
as a System.Object, while the CLR takes care of the memory-related details on our behalf.
To review the boxing process, assume you have created a System.Collections.ArrayList to hold
numeric (stack-allocated) data. Recall that the members of ArrayList are all prototyped to receive
and return System.Object types. However, rather than forcing programmers to manually wrap the
stack-based integer in a related object wrapper, the runtime will automatically do so via a boxing
operation:
static void Main(string[] args) { // Value types are automatically boxed when // passed to a member requesting an object. ArrayList myInts = new ArrayList(); myInts.Add(10); Console.ReadLine(); }
If you wish to retrieve this value from the ArrayList object using the type indexer, you must
unbox the heap-allocated object into a stack-allocated integer using a casting operation:
static void Main(string[] args) { ... // Value is now unboxed...then reboxed! Console.WriteLine("Value of your int: {0}", (int)myInts[0]); Console.ReadLine(); }
When the C# compiler transforms a boxing operation into terms of CIL code, you find the box opcode is used internally. Likewise, the unboxing operation is transformed into a CIL unbox operation.
Here is the relevant CIL code for the previous Main() method (which can be viewed using
ildasm.exe):
.method private hidebysig static void Main(string[] args) cil managed { ... box [mscorlib]System.Int32 callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object) pop ldstr "Value of your int: {0}" ldloc.0 ldc.i4.0 callvirt instance object [mscorlib] System.Collections.ArrayList::get_Item(int32) unbox [mscorlib]System.Int32 ldind.i4 box [mscorlib]System.Int32 call void [mscorlib]System.Console::WriteLine(string, object) ... }
Note that the stack-allocated System.Int32 is boxed prior to the call to ArrayList.Add() in
order to pass in the required System.Object. Also note that the System.Object is unboxed back into
a System.Int32 once retrieved from the ArrayList using the type indexer (which maps to the hidden
get_Item() method), only to be boxed again when it is passed to the Console.WriteLine() method.
|

