Boxing & Unboxing
Boxing is a process of converting a value type instance to a reference type instance. Let’s look at an example
int x = 13;
object o = x; // implicit boxing
In C# as we know we have two kinds of types, one is reference types and the other is value type which includes most of the integral types (int, long, double etc) and struct. In ECMA-335 it says
For every value type, the CTS defines a corresponding reference type called the boxed type. The reverse is not true: In general, reference types do not have a corresponding value type. The representation of a value of a boxed type (a boxed value) is a location where a value of the value type can be stored. A boxed type is an object type and a boxed value is an object.
All value types have an operation called box. Boxing a value of any value type produces its boxed value; i.e., a value of the corresponding boxed type containing a bitwise copy of the original value.
One of the main memory-related rules in .NET world is — avoid boxing. A massive boxing code could indeed cause us performance problems. Unfortunately, most boxing is done implicitly so we may be even not aware of it. Thus, it is worth it to remember common places when such implicit boxing can occur:
- Value type is used where object (reference type) is expected — thus it needs to be boxed. As a general rule, it is good to avoid methods taking objects as parameters, if possible.
int x= 99;
return string.Format("{0}", x); //boxing
- Value type instance is used as any interface type implemented by this value type. As the interface is a reference type, we also need boxing here. In other words converting a struct to an interface causes boxing. Calling an implicitly implemented member on a struct does not cause boxing:
interface I
{
void Foo();
}
struct S : I
{
public void Foo() {}
}
...S s = new S();
s.Foo(); // No boxing.
I i = s; // Box occurs when casting to interface. i.Foo();
Another example
public string Main(string args)
{
MyContactStruct contact;
var msg = GetName(contact); //boxing
}string GetName(IContactInterface contact)
{
return contact.Name;
}public struct MyContactStruct
{
// some properties
}
We can avoid boxing in such cases by introducing a generic method that will expect a desired interface as a generic type parameter:
string GetName<T>(T contact) where T: IContactInterface
{
return contact.Name;
}
Let’s look at the one of the most common sources of boxing, which comes from the fact of a value type being used as an interface — foreach instruction on IEnumerable<T>
. In such a case we are passing List<int>
instance as an IEnumerable<int>
to Print
method. In the Print
method, list collection is seen as IEnumerable<int>
so IEnumerable<int>.GetEnumerator()
will be called, which is expected to return IEnumerator<int>. List<T>
implements IEnumerable<int>
obviously but the important fact is that GetEnumerator()
returns Enumerator, which is… struct. As this struct is being used as IEnumerator<int>
, boxing happened once at the beginning of the foreach
loop.
public int Main(string args)
{
List<int> list = new List<int>() {1, 2, 3};
Print(list);
return list.Count;
}public void Print(IEnumerable<int> list)
{
foreach (var x in list)
{
Console.WriteLine(x);
}
}
If you would like to avoid boxing, you may simply pass list as List<int>
to Print method (making it public void Print(List<int> list)
)