Why Do We Need Boxing and Unboxing in C#

Why do we need boxing and unboxing in C#?


Why

To have a unified type system and allow value types to have a completely different representation of their underlying data from the way that reference types represent their underlying data (e.g., an int is just a bucket of thirty-two bits which is completely different than a reference type).

Think of it like this. You have a variable o of type object. And now you have an int and you want to put it into o. o is a reference to something somewhere, and the int is emphatically not a reference to something somewhere (after all, it's just a number). So, what you do is this: you make a new object that can store the int and then you assign a reference to that object to o. We call this process "boxing."

So, if you don't care about having a unified type system (i.e., reference types and value types have very different representations and you don't want a common way to "represent" the two) then you don't need boxing. If you don't care about having int represent their underlying value (i.e., instead have int be reference types too and just store a reference to their underlying value) then you don't need boxing.

where should I use it.

For example, the old collection type ArrayList only eats objects. That is, it only stores references to somethings that live somewhere. Without boxing you cannot put an int into such a collection. But with boxing, you can.

Now, in the days of generics you don't really need this and can generally go merrily along without thinking about the issue. But there are a few caveats to be aware of:

This is correct:

double e = 2.718281828459045;
int ee = (int)e;

This is not:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)o; // runtime exception

Instead you must do this:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)(double)o;

First we have to explicitly unbox the double ((double)o) and then cast that to an int.

What is the result of the following:

double e = 2.718281828459045;
double d = e;
object o1 = d;
object o2 = e;
Console.WriteLine(d == e);
Console.WriteLine(o1 == o2);

Think about it for a second before going on to the next sentence.

If you said True and False great! Wait, what? That's because == on reference types uses reference-equality which checks if the references are equal, not if the underlying values are equal. This is a dangerously easy mistake to make. Perhaps even more subtle

double e = 2.718281828459045;
object o1 = e;
object o2 = e;
Console.WriteLine(o1 == o2);

will also print False!

Better to say:

Console.WriteLine(o1.Equals(o2));

which will then, thankfully, print True.

One last subtlety:

[struct|class] Point {
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

Point p = new Point(1, 1);
object o = p;
p.x = 2;
Console.WriteLine(((Point)o).x);

What is the output? It depends! If Point is a struct then the output is 1 but if Point is a class then the output is 2! A boxing conversion makes a copy of the value being boxed explaining the difference in behavior.

Boxing and unboxing: when does it come up?

It's much less of an issue now than it was prior to generics. Now, for example, we can use:

List<int> x = new List<int>();
x.Add(10);
int y = x[0];

No boxing or unboxing required at all.

Previously, we'd have had:

ArrayList x = new ArrayList();
x.Add(10); // Boxing
int y = (int) x[0]; // Unboxing

That was my most common experience of boxing and unboxing, at least.

Without generics getting involved, I think I'd probably say that reflection is the most common cause of boxing in the projects I've worked on. The reflection APIs always use "object" for things like the return value for a method - because they have no other way of knowing what to use.

Another cause which could catch you out if you're not aware of it is if you use a value type which implements an interface, and pass that value to another method which has the interface type as its parameter. Again, generics make this less of a problem, but it can be a nasty surprise if you're not aware of it.

Why do some languages need Boxing and Unboxing?


what is so hard technically to include primitive types when talking about Generics ?

In Java's case, it's because of the way generics work. In Java, generics are a compile-time trick, that prevents you from putting an Image object into an ArrayList<String>. However, Java's generics are implemented with type erasure: the generic type information is lost during run-time. This was for compatibility reasons, because generics were added fairly late in Java's life. This means that, run-time, an ArrayList<String> is effectively an ArrayList<Object> (or better: just ArrayList that expects and returns Object in all of its methods) that automatically casts to String when you retrieve a value.

But since int doesn't derive from Object, you can't put it in an ArrayList that expects (at runtime) Object and you can't cast an Object to int either. This means that the primitive int must be wrapped into a type that does inherit from Object, like Integer.

C# for example, works differently. Generics in C# are also enforced at runtime and no boxing is required with a List<int>. Boxing in C# only happens when you try to store a value type like int in a reference type variable like object. Since int in C# inherits from Object in C#, writing object obj = 2 is perfectly valid, however the int will be boxed, which is done automatically by the compiler (no Integer reference type is exposed to the user or anything).

Where do we use Boxing or what's the use of boxing?

In C#, you usually don't know when boxing is happening and at least with .NET 2.0 generics, boxing is not needed as often (in .NET 1.x, if you wanted to have an ArrayList of integers, you'd have to box them first because ArrayList only works with objects).

But the main place you'll be using boxing is in function calls that accept only objects. The most obvious example of that is Console.WriteLine (or string.Format) which takes the arguments as objects. For example:

int n = 1234;
Console.WriteLine("A number: {0}", n);

The integer n is boxed to a object reference in order to be passed to the Console.WriteLine method.

What does Box and Unbox mean?

Here is a more detailed explanation that looks at the internal of Common Language Runtime.

First, let's make the difference between value types and reference types:

  • A value type is held on the stack and a copy of it is passed to called methods
  • A reference value is held in the managed heap and the stack holds only a pointer (reference) to its location. The location, and not the object, is passed to called methods

If you don't know what the stack is (don't be offended), it's a memory area that holds local variables in a method and addresses of caller functions used for return instruction (just to be brief and provide a general answer). When you call a method, a sufficient area on the stack is statically allocated to it, so stack allocation is always called static allocation.

The heap, instead, is a memory area separated from the stack, property of the running process, in which allocation must be first demanded to the operating system, and that's why it's called dynamic allocation (if you don't run in an if statement, for example, memory may not be allocated for your process, instead stack is always allocated).

Just to make a final example on heap and stack: in languages such as C++, declaring int[100] a; statically allocates 100*8 bytes on the stack (64-bit system assumed), while int* a = new int[100]; declares a 8 bytes (on 64-bit systems) area on the stack AND requests 800 more bytes on the heap, if and where available.

Now let's talk about C#:

Boxing

Since int is a value type, and is allocated on the stack, when you cast it to object or any other reference type (actually there is no other reference type from which int can inherit, but it's a general rule) the value must become necessarily a reference type. So a new area on the heap is allocated, the object is boxed inside it and the stack holds a pointer to it.

Unboxing

Just the opposite: when you have a reference type, such as object, and want to cast it to a value type, such as to int, the new value must be held on the stack, so CLR goes to heap, un-boxes the value and copies it to the stack.

In other words

Remember the int[] and int* examples? Simply, when you have int in C#, the runtime expects its stack location to hold the value but instead when you have object, it expects its real value to be in the heap location pointed by the stack.

What does Box and Unbox mean?

Here is a more detailed explanation that looks at the internal of Common Language Runtime.

First, let's make the difference between value types and reference types:

  • A value type is held on the stack and a copy of it is passed to called methods
  • A reference value is held in the managed heap and the stack holds only a pointer (reference) to its location. The location, and not the object, is passed to called methods

If you don't know what the stack is (don't be offended), it's a memory area that holds local variables in a method and addresses of caller functions used for return instruction (just to be brief and provide a general answer). When you call a method, a sufficient area on the stack is statically allocated to it, so stack allocation is always called static allocation.

The heap, instead, is a memory area separated from the stack, property of the running process, in which allocation must be first demanded to the operating system, and that's why it's called dynamic allocation (if you don't run in an if statement, for example, memory may not be allocated for your process, instead stack is always allocated).

Just to make a final example on heap and stack: in languages such as C++, declaring int[100] a; statically allocates 100*8 bytes on the stack (64-bit system assumed), while int* a = new int[100]; declares a 8 bytes (on 64-bit systems) area on the stack AND requests 800 more bytes on the heap, if and where available.

Now let's talk about C#:

Boxing

Since int is a value type, and is allocated on the stack, when you cast it to object or any other reference type (actually there is no other reference type from which int can inherit, but it's a general rule) the value must become necessarily a reference type. So a new area on the heap is allocated, the object is boxed inside it and the stack holds a pointer to it.

Unboxing

Just the opposite: when you have a reference type, such as object, and want to cast it to a value type, such as to int, the new value must be held on the stack, so CLR goes to heap, un-boxes the value and copies it to the stack.

In other words

Remember the int[] and int* examples? Simply, when you have int in C#, the runtime expects its stack location to hold the value but instead when you have object, it expects its real value to be in the heap location pointed by the stack.



Related Topics



Leave a reply



Submit