Boxing

Sun, July 31, 2005, 04:06 PM under dotNET
If I am interviewing you for a dotnet position and I ask you about boxing I expect you to be able to quickly describe what it is, why one should care and give me a quick example. If I see you struggle, I'll help you by asking "If I add an integer to an ArrayList, does boxing occur?". If you hesitate answering that then... [this really happened recently when I interviewed someone for a senior .NET position].

So it was with pleasure that I saw a post on boxing this week. If you are struggling with the concept go read Raymond Lewallen's Boxing and Unboxing for Beginners.

I don't really have anything to add to that post other than a couple of comments on the Intermediate Language:
1. Note the IL instruction box. That is where boxing gets its name (should be obvious but thought I'd spell it out :-)
2. Raymond's example uses VB and unfortunately has Option Strict Off. If it had Option Strict On, you'd see that when you retrieve the result you have to cast it to the integer
i.e. b = DirectCast(a(0), Int32)

If you make that modification, then you see the other magic IL instruction: unbox (rather than the *really* ugly VB-compiler-injected-statement:
[Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.IntegerType::FromObject(object)).

Here is the IL with that small modification for VB in VS.NET 2003 (note that it is compiled in Release mode so we get cleaner IL):
.method public instance void  Method1() cil managed
{
// Code size 39 (0x27)
.maxstack 2
.locals init (class [mscorlib]System.Collections.ArrayList V_0,
int32 V_1)
IL_0000: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldc.i4.s 10
IL_0009: box [mscorlib]System.Int32
IL_000e: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
IL_0013: pop
IL_0014: ldloc.0
IL_0015: ldc.i4.0
IL_0016: callvirt instance object [mscorlib]System.Collections.ArrayList::get_Item(int32)
IL_001b: unbox [mscorlib]System.Int32
IL_0020: ldobj [mscorlib]System.Int32
IL_0025: stloc.1
IL_0026: ret
} // end of method Form1::Method1
3. Just so you can see the IL for a C# version compiled under Visual Studio 2005 Beta 2, here is some code:
private ArrayList ar = new ArrayList();
public void Box() {
ar.Add(5);
}
public void Unbox() {
int i = (int)ar[0];
}
...and equivalent IL:
.method public hidebysig instance void  Box() cil managed
{
// Code size 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.ArrayList BoxUnbox.Form1::ar
IL_0006: ldc.i4.5
IL_0007: box [mscorlib]System.Int32
IL_000c: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
IL_0011: pop
IL_0012: ret
} // end of method Form1::Box

.method public hidebysig instance void Unbox() cil managed
{
// Code size 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.ArrayList BoxUnbox.Form1::ar
IL_0006: ldc.i4.0
IL_0007: callvirt instance object [mscorlib]System.Collections.ArrayList::get_Item(int32)
IL_000c: unbox.any [mscorlib]System.Int32
IL_0011: pop
IL_0012: ret
} // end of method Form1::Unbox
By the way, a great follow up to the above is to show how Generics eliminate the need for boxing in such a scenario pointing out that
a) No boxing/unboxing penalty takes place
b) No cast is required
c) Compiler prevents anyone adding objects of other types to our List