Main Site Documentation

How to not Box or Unbox anything?


#1

Recently moved apartments. Still unboxing all my stuff. And I say enough!

I’ve been reading about boxing and unboxing in C# and how it is mostly a relic with a few purposes here and there.

I get what it is but I can’t find examples of how it enters into your program when you aren’t using ArrayList, HashTable, or invoking a method with an object as a parameter.

What I can’t seem to find is a comprehensive list of what is going to box and what is going to unbox. Does anyone know a good place to read about how to avoid it? Like maybe with some examples of a solution with and without boxing or unboxing?

Also how can I see the IL? It appears from some sources that I could count the number of “box” in the IL to get an idea of whether I’m taking performance hits or not. (.netmf 4.3)


#2

Since boxing involves allocation of an object in the managed heap, it is an expensive operation.

But, it is not easy to create boxing without knowing it is happening.

I would not worry about over use of boxing.


#3

I wouldn’t exactly call boxing a relic. It’s used any time a value type is stored in an object or accessed as an interface it implements. You will see it more in TinyCLR and NETMF given the lack of generics. The big one you called out is ArrayList, HashTable, and so on. Storing an int in there for example boxes the int.

Value types in C# are structs while reference types are classes. So boxing will only happen when you use a struct. int, double, IntPtr, and enum are all structs, so also value types.

In many desktop implementations of the CLR value types themselves are not stored on the heap until they are boxed. They usually exist on the stack as arguments or locals or embedded in a reference type. In our implementations here, as of today, value types are also allocated from the same managed heaps as reference types so the distinction is blurred a bit in practice. Of course, value type semantics are preserved such as receiving a copy and not a reference (unless you explicitly use ref).

You can take a look at the IL to see the box and unbox instructions. In the Developer Command Prompt for VS 2017 that gets installed with VS, you can run ildasm to open a managed assembly and see its IL code. Keep in mind that managed assemblies are post-processed before being sent to the device.

As @Mike was getting at, I would not worry too much about boxing and unboxing outside of specific benchmarks and use cases that identify it as a bottleneck.

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing is a good starting point reference.


#4

Okay thanks for the assurance.

Do you know if any or all of these statements are correct?

public class IntHolder
{
    public int MyInteger { get; set; }
}

public class Program
{
    static IntHolder IntHolderInstance;

    public void Main()
    {
        IntHolderInstance = new IntHolder();
        IntHolderInstance.MyInteger = 5;

        Debug.Print(IntHolderInstance.MyInteger.ToSTring());
    }
}

The above program has no boxing or unboxing.

public class Program
{
    static IntHolder IntHolderInstance;

    public void Main()
    {
        IntHolderInstance = new IntHolder();
        int x = 5;
        IntHolderInstance.MyInteger = x;

        Debug.Print(IntHolderInstance.MyInteger.ToSTring());
    }
}

The above program will box the value of x when assigning the object’s property.

 public class Program
{
    static IntHolder IntHolderInstance;

    public void Main()
    {
        IntHolderInstance = new IntHolder();
        IntHolderInstance.MyInteger = 5;
        
        int x = IntHoldeInstance.MyInteger;
        Debug.Print(x.ToSTring());
    }
}

The above program will unbox the value of IntHolderInstance.MyInteger when assigning to x.

Okay I typed all this out while John was making his post, so now I’m not sure about this but I’ll post it anyway.Thanks for the method of viewing IL.


#5

Well there’s no sign of
"box" or “unbox” anywhere in the IL. Not sure that means what I think it does, but I think I can put this to rest and not worry about it.


#6

In the three examples you showed above, only the statement you made about the first code snippet was correct.

For boxing to occur, the target must be an object, and the source a value type such as integer. A integer value within a class/object is still an integer value, not an object.