In this article I will try to explain the concepts of Boxing and Unboxing. There are two types of objects within the .Net framework, value types and reference types. Value types are stored on the stack while reference types are always stored on the heap. Converting a value type into a reference type is called Boxing while converting it back from a reference type to a value type is called Unboxing.
Value types are primitive types like System.Int32 (Integer in VB, int in C#) or System.Double. These types are mapped directly to the Framework Class Library (FCL) and are all derived from System.ValueType. Apart from the regular primitive types (integer, long, single, double, char, and so on) Enumerations and Structures also inherits from System.ValueTypes so they are also stored on the stack.
All classes are reference types and they are stored on the heap. So when you create a new instance of a class using the new keyword, memory are allocated on the heap for the object and the memory address is returned. Strings are a special case since they are also reference types, even though they are often treated as if they where value types, but that’s not the case. The difference is that you don’t have to create an instance of a string in the same manner as you would with another class.
Reference types are also Nullable, meaning that you can set the reference to Null (Nothing in VB).
Widening and Narrowing conversions
Before we go into the Boxing and Unboxing I would like to take a moment to explain the difference between a widening and a narrowing conversion. A widening conversion is always safe since there is never any risk of any data loss. For example converting an Integer into a Long (System.Int64) is a widening conversion. There is no integer value that doesn’t fit into a Long so you can always do that. However doing the opposite, converting a Long into an Integer is more risky since there are a chance that you will lose some data, since a Long can contain a number that is too high (or too low) to fit into an integer.
You can always use an implicit conversion when you do a widening conversion.
Dim i As Integer = 3 Dim l As Long l = i 'Implicit conversion
However to do the opposite, a narrowing conversion, you need to do an explicit conversion.
i = CType(l, Integer) 'Explicit conversion
'or i = CInt(l) 'Explicit conversion
If you don’t do an explicit conversion when you’re doing a narrowing conversion you will get a compilation error so your project will not compile (Note, VB can allow you to do an implicit conversion if you have Option Strict set to Off, something I strongly advice you not to have, C# however will never allow it). If the Long value does not fit into the Integer you will however still get a runtime error since the system will then throw a System.OverflowExeption. So when you do a narrowing conversion you must be sure that it can be done.
As mentioned earlier Boxing refers to the conversion of a value type to a reference type.
Dim i As Integer = 3 Dim o As Object o = i 'Boxing the Integer into an object (reference type)
Above I do an implicit conversion of an integer to an object. This is possible since it’s a widening conversion so there is no risk of any data loss. However doing the opposite, converting the object to an integer is a narrowing conversion, where there is a risk that data will be lost.
i = o 'Implicitly doing a narrowing conversion (not allowed) i = CInt(o) 'Explicitly doing a narrowing conversion (allowed)
In C# if you have boxed a particular type you must unbox it to the same type. You can not box an integer to an object and then unbox it to a long.
int i = 3; long l; object o; o = i; l = (long)o; //will throw a System.InvalidCastException
int i = 3; long l; object o; o = i; l = (long)(int)o;
However the VB’s type conversion functions (CType and the more specific CInt, CLng, CDbl and so on) does allow this.
Dim i As Integer = 3 Dim l As Long Dim o As Object o = i l = CLng(o) 'allowed
I hope that you by now have a better understanding of Boxing and Unboxing. So let me end this article by saying that you should avoid doing this if possible. Doing type conversion is time consuming, and that is especially true when it comes to Boxing and Unboxing since data have to be transferred back and forth between the stack and the heap. Using Generics, which has existed in .Net since version 2.0, is a much better approach than having to deal with value type to reference type conversion.
I will cover Generics in a later article.