#1,052 – Boxing Is a Kind of Implicit Conversion

Boxing is the process of copying a value-typed object to a new instance of a reference-typed object.  Boxing operations are classified as implicit conversions, in that you are converting between types in a way that is guaranteed to succeed and does not require a cast operator.

Below are some examples of boxing operations.

public interface IDistance
{
double CalcDistance();
}

public struct Point3D : IDistance
{
public double X, Y, Z;

public double CalcDistance()
{
return Math.Sqrt(X*X + Y*Y + Z*Z);
}
}

public enum Mood { Crabby, NotSoCrabby };

static void Main(string[] args)
{
// Three kinds of value-typed objects
Point3D pt;
pt.X = 1.0;
pt.Y = 2.0;
pt.Z = 3.0;

int i1 = 12;
int? i2 = null;

// Convert to object
object o = pt;
o = i1;
o = i2;

// Convert to dynamic
dynamic dyn1 = pt;
dyn1 = i1;
dyn1 = i2;

// Convert to System.ValueType
ValueType vty = pt;
vty = i1;
vty = i2;

// Convert to interface implemented
// by the value type
IDistance idist = pt;
double dist = idist.CalcDistance();

// From enum value to System.Enum
Mood myMood = Mood.NotSoCrabby;
Enum genEnum = myMood;
}

#658 – What Boxing and Unboxing Look Like in IL

Recall that your C# source code is compiled to a platform-neutral intermediate language called Common Intermediate Language.  You can view the IL for your application, after building the code, using the IL Disassembler tool.

If you look at the IL generated for code that boxes or unboxes a value, you’ll see unique CIL instructions for boxing (box) and unboxing (unbox).

Assume that you box an int value and then later unbox it.

int x = 12;

object o = x;    // Box

int y = (int)o;  // Unbox

If you build this code and look at the IL, you’ll see the box and unbox instructions. #657 – Boxing Makes a Copy of An Object

When you pass a value-typed object to a function that accepts a parameter of type object, the value-typed object is boxed.  That is, it is copied into a temporary reference-typed object, which is actually passed into the function.  This boxing happens automatically.

If we look at an object that represents a boxed value type, we’ll see that its reported type is the value type.

static void Test(object o)
{
Console.WriteLine(string.Format("Value = {0}, Type = {1}", o, o.GetType()));
}

static void Main()
{
int x = 12;
Test(x);
} However, if we try changing the object within the method, the original int that was passed in is unaffected.

static void Test(object o)
{
Console.WriteLine(string.Format("Value = {0}, Type = {1}", o, o.GetType()));
o = 100;
}

static void Main()
{
int x = 12;
Test(x);
Console.WriteLine(string.Format("After call, x = {0}", x));
} The boxed object is passed into the function using value type semantics.

#577 – Using the is Operator to Check for an Unboxing Conversion

You can use the is operator to check to see whether an object can be unboxed to a particular type.  The operator will return true if the object contains a boxed instance of the specified type.

int i = 12;
double d1 = 4.2;

object o1 = i;   // Boxed int
object o2 = d1;  // Boxed double
object o3 = new object();  // Some object

bool check;

check = o1 is int;       // true - object contains a boxed int
check = o2 is int;       // false - object contains boxed double
check = o3 is int;       // false

#221 – When Unboxing, You Must Match the Original Type Exactly

We know that we can do explicit casts when assigning from one type to another and the assignment will work if the value being assigned can be represented by the target type.

int i = 1964;
uint ui = (uint)i;  // cast works fine

But when casting as part of an unboxing operation, the target type must match the original type exactly.

int i = 1964;
object o = i;    // box

// Attempted unbox to different type
// Throws InvalidCastException
uint ui = (uint)o;

This cast, as part of the unboxing operation, is allowed at compile time, but throws an InvalidCastException at run-time.

#220 – Boxing Can Happen Automatically

Boxing can happen automatically in C#, when you pass a value-typed variable to a method that accepts a System.Object (object).

The Console.WriteLine method is a good example of this.  It accepts a format string as its first parameter and then a series of object parameters, which represent objects to substitute into the format string.  (Specifically, Console.WriteLine calls each object’s ToString method to get its string representation).

We can pass in some reference-typed objects to Console.WriteLine.

Person p = new Person("Lillian", "Gish");

Console.WriteLine("The actor known as {0} was a silent film star.", p);

We can also pass value-typed variables into Console.WriteLine.  The value types will be boxed–converted into reference types.  An object will be created on the heap for each value-typed parameter and its value copied to the new object.

int i = 1964;

Console.WriteLine("Favorite numbers: {0}, {1}, {2}", 42, 6, i);

#219 – Unboxing a Boxed Object

If you’ve converted a value-typed object to a reference-typed object by boxing it, you can later unbox the object, converting it back to a value type.

Assume that we’ve boxed an integer value as follows:

int i = 1964;
object o = i;   // Box i

Now we have a copy of the integer in a new object on the heap. We can later convert this object back to a value type.  This is known as unboxing.

int j = (int)o;  // Unbox

When we unbox, we need to explicitly cast the object to the value type.  Because the integer derives from System.Object, we can convert it to an object implicitly, but when converting from an object to an int, we need an explicit cast.

As with boxing, unboxing copies the value of the heap-based object.  It does not delete the object on the heap or free its memory.

#218 – Store Value-Typed Objects on the Heap Through Boxing

Value-typed objects are typically stored on the stack, while reference-typed objects are stored on the heap.  You can, however, convert an instance of a value type to a reference type object through a process known as boxing.

In the example below, we box i, assigning it to a reference type variable.  Because i derives from System.Object (object), as do all types in .NET, we can assign i to o without doing a cast.  A new object is created on the heap, the value of i is copied into it, and the variable o is set to reference the new object.

int i = 46;
object o = i;   // Box i

Because a copy of the value-typed object is made, you can change the original object without changing the new object on the heap.

i = 47;   // i now 47, but o still points to object with value of 46