In this article, we are going to talk about boxing and unboxing in C#.
When we work with C# language constantly, we need to convert data between different data types. For this reason, sometimes we have to use boxing and unboxing when we need to convert a value type to an object type or vice versa.
So, let’s discuss each of these concepts with some examples.
Overview of Value Types and Reference Types
C# is a type-safe programming language containing value types and reference types. Each of them is allocated in different portions of memory: stack and heap.
A value type variable directly contains its data and holds the data within its own memory location. On the other hand, a reference type variable stores a reference to their data (objects). It contains a pointer to another memory location that holds the real data.
In C#, all types inherit from
System.Object type, directly or indirectly. This means it’s possible to assign values of any type to
object type variables.
NOTE: The object type is an alias to
System.Object in .NET, and an object variable is always a reference type.
There are built-in value types in C# like
char among others, that correspond to the primitive types in .NET. Among built-in reference types, we have
array, and especially
Boxing is the process to perform a conversion of a value type to an object type and is an implicit process.
Let’s see an example of boxing from an
int variable to an
int valueType = 25; object objectType = valueType;
Here we declare an
int variable named
valueType and initialize it with the value of 25. After that, we create an object variable named
objectType and assign the
int value. As result, the value type variable
valueType is copied to the
reference type variable
Boxing is implicit because we can do it directly:
object objectType = valueType;
Actually, we also can perform the boxing explicitly, but it is never required:
object objectType = (object)valueType;
We also can perform boxing through a method:
public object BoxToInt(int value) => value;
In this method, we have an
int parameter and we return it as an object type.
Unboxing is basically the opposite of boxing. It’s the conversion from an object type to a value type and unlike the boxing process, it is an explicit process.
Let’s consider the same
int variable type, that we used in the boxing example, to perform the unboxing:
int valueType = 25; object objectType = valueType; //boxing int unboxedValue = (int)objectType //unboxing
We declare a new int variable named
unboxedValue, and we typecast the
objectType variable to an int value:
In this case, the conversion must be explicit because the conversion from object to int needs a type casting:
Otherwise, we’ll have a compiler error CS0266:
Cannot implicitly convert type 'object' to 'int'. An explicit conversion exists (are you missing a cast?)
Another important point! We can’t perform an unboxing of a boxed variable to a different type:
int valueType = 25; object objectType = valueType; //boxing short unboxedValue = (short)objectType; //unboxing
In this case, we get a System.InvalidCastException:
Unable to cast object of type 'System.Int32' to type 'System.Int16'.
Finally, we also can use methods for performing unboxing of different types:
public int UnboxToInt(object o) => (int)o;
So, here we do the reverse process, where we receive the parameter
o , which is of
object type and as result, we have an
Additional Info About Boxing and Unboxing
When boxing occurs an object reference is created on the stack memory that references a value of the type on the heap memory.
On the other hand, the unboxing copies the value from the instance into a value type variable.
Both are expensive computational processes, so it is highly recommended to use them just when necessary.
Another care to be taken is that sometimes we could perform, for example, an unboxing without us noticing. It’s the case of the
String.Concat method, that is used frequently:
string name = "Joe Doe"; Console.WriteLine(string.Concat(name,"'s age: ", 31)); /* Output: Joe Doe's age: 33 */
Here we concatenate three variables, two of the
string type and one of the
But, if we inspect the definition of the
public static string Concat(object? arg0, object? arg1, object? arg2) => Concat(arg0?.ToString(), arg1?.ToString(), arg2?.ToString());
We can see it has twelve different overloads, but paying attention to this specific case, which corresponds to what we are using, we’ll see that all parameters are of the object type and there are unboxing processes happening here.
Let’s consider another example using an ArrayList:
var myArrayList = new ArrayList(); myArrayList.Add(1); // int value myArrayList.Add("Joe Doe"); // string value myArrayList.Add(1.23); // double value foreach (var item in myArrayList) Console.WriteLine(item);
Again, inspecting the definition of the
public virtual int Add(object? value)
We can notice an unboxing here.
public virtual void Push(object? obj) // Stack's method public virtual void Enqueue(object? obj) // Queue's method public virtual void Add(object key, object? value) // Hashtable's method
In this article, we have learned about boxing and unboxing in C#, and how to use both. Also, we’ve mentioned some additional info that we need to consider while working with different C# classes or methods.