Unboxing is where you can fall into some troubled waters; as long the type you're unboxing to is correct everything is fine, but should you get it wrong you'll get an invalidCastException during, which can suck.
Let's take a quick look at the "as" operator, the as operator lets us convert variables into nullable types.
namespace pav.boxing
{
class Program
{
static void Main(string[] args)
{
// boxing
object o = 32;
// unboxing
int i = (int)o;
//throws System.InvalidCastException
bool b = (bool)(o);
}
}
}
Luckily we have the "is" comparison, which we can do on reference types such as object allowing us to test and see if our reference type is in fact the value type we want to convert to.
namespace pav.boxing
{
class Program
{
static void Main(string[] args)
{
// boxing
object o = 32;
if (o is bool)
{
//this will never fire because our object is not a bool
var b = (bool)o;
}
}
}
}
The "is" comparison also works when comparing a reference type to another reference type,
namespace pav.boxing
{
class Person { }
class Employee : Person { }
class Program
{
static void Main(string[] args)
{
object o = 32;
if (o is int) //true
Console.WriteLine("o is an int");
else
Console.WriteLine("o is not an int");
if (o is Person) //false
Console.WriteLine("o is a person");
else
Console.WriteLine("o is not a person");
var p = new Person();
var e = new Employee();
if (e is Person) // true
Console.WriteLine("e is a Person");
else
Console.WriteLine("e is not a person");
if (p is Employee) // false
Console.WriteLine("p is an employee");
else
Console.WriteLine("p is not an employee");
}
}
}
Not only can you use it to identify if your variable is of a particular type but also if it's of a base type, in our example, our instance of Employee e is a Person but our instance of Person p is not an Employee. Keep in mind that when casting between reference types, this is not considered 'boxing' or 'unboxing' these terms are reserved for value types to reference types and vice-versa. When converting between reference types it is considered 'upcasting' or 'downcasting' this is because at no point is new memory allocated.
Let's take a quick look at the "as" operator, the as operator lets us convert variables into nullable types.
static void Main(string[] args)
{
object o = 32;
//build time error, not allowed because int is not nullable
var i = o as int;
}
since value types are not nullable, they can't be converted by using the "as" operator. however reference types are nullable.
so when we successfully convert a type using the as operator we receive our target type, however is the conversion fails we get a null. A subtype can be converted into a base type, but not vice-versa.
namespace pav.boxing
{
class Person { }
class Employee : Person { }
class Program
{
static void Main(string[] args)
{
object o = new Employee();
var e = o as Employee;
if (e != null)
Console.WriteLine("o is an employee");
var p = o as Person;
if (p != null)
Console.WriteLine("o is a person");
e = new Person() as Employee;
if (e != null)
Console.WriteLine("e is an employee");
else
Console.WriteLine("e is not an employee");
}
}
}
To sum it up, in C#, 'boxing' is to the process of converting a value type (such as an int or a struct) to a reference type (such as an object). This is done by creating a new object and copying the value of the value type into the new object. 'Unboxing' is the opposite process, where a reference type (such as an object) is converted back to a value type. This is done by copying the value stored in the object back into a value type variable.
Boxing and unboxing can have a performance cost, as they involve creating and manipulating objects in memory. Therefore, it is generally recommended to avoid unnecessary boxing and unboxing in performance-critical code.
One thing to keep in mind is that technically since strings and classes are already reference types, they cannot be 'boxed'. Only value types such as int, float, structs, etc can be 'boxed', this is because they are stored on the stack and not the heap like reference types.
When you try to box a reference type, it will just return a reference to itself and does not create a new object. However, even though reference types cannot be boxed, reference types can be stored as objects, just keep in mind that this does not the qualify as 'boxing'. When casting a reference type such as a string or a class to an object this is considered downcasting, whereas converting an object back to a string or class is considered upcasting.