How To Avoid Boxing And Unboxing In C#

As a C# Performance Architect, your job is to create solution architectures that provide the best possible performance for your business or organization. And to do your job well, you’ll need a solid understanding of basic C# code optimizations.

In this post, we’ll look at a process called ‘Boxing’, which blurs the line between value types and reference types. Unexpected boxing can slow down your code a lot, so it’s very important to have a clear understanding of what boxing is and when it happens.

Take a look at the following code.

static void Main(string[] args)
{
    int a = 1234;
    object b = a; // <--- how can this work?
}

I have a variable a containing the value 1234. Then I declare a second variable b of type object and assign a to b. This should work, because in C# all types inherit from object, including integers. So I can assign anything to my object type variable.

But wait! Integers are value types, and objects are reference types. So in memory, my variables are stored like this:

Integers are value types, so they store their value inline with the variable itself. I’ve declared a as a local variable so it lives on the stack, and because it’s a value type, the value ‘1234’ is also stored on the stack.

Objects are reference types, so they store a reference to heap memory where the object data is stored. I’ve declared b as a local variable, so the reference itself is stored on the stack, but the reference can only point to a location in heap memory.

So now we have a problem. The number 1234 is stored on the stack, but objects can only refer to heap memory. How is the assignment possible?

To make this work, the framework uses a process called boxing. Here’s what that looks like in memory:

The .NET Framework copies the integer value from the stack, packs it into an object, and stores it on the heap. Now, b has something to refer to – the new integer object on the heap.

Boxing happens every time behind the scenes when you have a variable, parameter, field or property of type object, and you assign value type data to it. Boxing is nice because it blurs the line between value types and reference types, but it can also be a pain because it introduces extra overhead and slows your code down.

You might be wondering if there is a corresponding process called ‘unboxing’?

Yes, there is. Check out this code:

static void Main(string[] args)
{
    int a = 1234;
    object b = a; // <--- boxing happens here

    int c = (int)b; // <--- but how can this work?
}

Here I declare a new integer variable c and cast the object value into it. But c is a value type on the stack, whereas b refers to a boxed integer on the heap. How does this work?

You’ve probably guessed that it will look something like this:

This process is called unboxing. The framework takes the packaged integer object on the heap, and copies its value back to the stack into variable c.

Boxing and unboxing operations are pretty fast, because they only copy a tiny amount of memory between the stack and the heap. But in mission-critical loops all those memory operations will start to add up. You will also be putting more pressure on the garbage collector to remove all the packaged value types from the heap.

In my benchmarks, I’ve found that boxing and unboxing can make your code run 5 times slower than normal.

But boxing is easy to avoid. Just stick to these three tips:

  • Avoid  object types in mission-critical code.
  • If you do have to use object types, don’t assign any value type data to them
  • And don’t use structs with interfaces in mission-critical code

Surprised by that third tip? Remember that structs are value types and an interface is a reference type. Casting a struct to an interface will trigger a boxing operation!

 

 

Would you like to know more? I’ve created a series of blog posts on C# performance optimization. Each post is based on content from my courses and from actual techniques I’ve used in the field.

Take a look: