Understanding Generics—Revisiting Boxing and Unboxing

Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio


Jump to: navigation, search
CSharp-Online.NET:Articles
C# Articles

Understanding Generics

© 2006 Andrew Troelsen

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.


Previous_Page_.gif Next_Page_.gif

Personal tools