Skip to content
Theory · Value and reference types · lesson 5 of 11

Boxing and unboxing

In the theme on collections, you met the type object. A variable of this type can be given a value of any type: a number, a string, an array, an object of your class. object is a reference type, and its variable holds a reference. But an int number lives right in its variable, and there is no reference to it. So how does it get into an object?

Picture a coin. The int coin lies in your palm, and an object variable can only hold a tag with the address of a box. So C# puts a copy of the coin into a box—a small object on the heap—and gives the object variable a reference to that box:

C#
int coins = 5;
object box = coins;
coins = 7;
Console.WriteLine(box); // 5
int back = (int)box;
Console.WriteLine(back + 1); // 6

Let's go through the example line by line:

  1. object box = coins; creates a box on the heap holding a copy of the number 5 and puts a reference to it into box.
  2. coins = 7 changes only the variable coins: the box holds a copy of its own.
  3. Console.WriteLine(box) prints 5—what is in the box.
  4. (int)box takes a copy of the number out of the box and puts it into back.
  5. The last line prints 6.

Do not confuse this box with the one from the theme on types: there the box was the variable itself. Here the box is a separate object on the heap, and the variable box holds only a tag with its address.

Here the analogy is inexact. A real coin in a box is the very coin that was in your hand. C# puts a copy into the box, so coins and the box live separately from then on.

Boxing

Boxing is turning a value of a value type into an object on the heap, for example when it is assigned to a variable of type object. It happens by itself: the line object box = coins; has no extra word in it.

Boxing also happens where you do not see it. A method with an object parameter accepts anything, and every number on its way in gets boxed:

C#
Show(42);   // 42
Show("hi"); // hi
Show(true); // True

void Show(object value)
{
    Console.WriteLine(value);
}

Let's look at the three calls:

  1. Show(42) boxes the number 42: the parameter value needs a reference.
  2. Show("hi") boxes nothing: string is a reference type already, and the reference to the string is copied into value.
  3. Show(true) boxes a bool—a value type too.

Unboxing

Unboxing is the reverse step: the cast (int)box checks that the box holds an int and copies the number out of it. Unboxing is always written explicitly. The line int back = box; does not compile: the compiler does not know what is in the object and demands a cast (error CS0266).

Unboxing opens a box with the type that lies in it. A boxed int is taken out as an int, not as a long or a double, and a boxed bool as a bool.

Why boxing matters

Every boxing creates a new object on the heap. One box goes unnoticed, but a million boxings in a loop make a million objects for the garbage collector to clean up later.

Now you can see what was going on in the ArrayList from the theme on collections. It stores its elements as object, so every number in it is boxed, and reading one back unboxes it with an (int) cast. List<int> stores the numbers themselves and needs no boxes.

Interviewers like to ask: what is boxing, and what is wrong with it? The answer has two parts:

  • boxing puts a copy of a value of a value type into a new object on the heap, and unboxing copies the value back with an explicit cast;
  • every box is one more object on the heap, so code that runs often avoids boxing: for example, it takes List<int> rather than ArrayList.

Common mistake

Someone tried to unbox a boxed int straight into a long:

C#
object box = 42;
long big = (long)box; // ✗
TEXT
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Int64'.

The program compiles, but when it runs, it stops on the second line. System.Int32 and System.Int64 in the message are the full .NET names of the types int and long.

Usually an int turns into a long with no cast at all. But unboxing is not a number conversion: box holds an int, and (long)box cannot open it. Unbox to int first, then widen to long: long big = (int)box;.

Key points

  • Boxing puts a copy of the value into an object on the heap when a value type is assigned to an object variable; it happens by itself.
  • Unboxing is written explicitly, (int)box, and it copies the value out of the box.
  • You unbox to the type that lies in the box: (long) on a boxed int throws an InvalidCastException.
  • Every boxing creates an object on the heap, which is why List<int> is better than ArrayList for numbers.