C# FAQ

Boxing and unboxing

Q: What does boxing and unboxing buy you?
A: Boxing and unboxing enable type system unification.

Q: What is type system unification?
The goal of type system unification is to bridge the gap between value types and reference types that exists in most languages. For example, a Stack class can provide Push and Pop methods that take and return object values.
   public class Stack
{
  public object Pop() {...}
  public void Push(object o) {...}
}
Because C# has a unified type system, the Stack class can be used with elements of any type, including value types like int.

Q: Does boxing and unboxing really bridge the gap between value types and reference types?
No, not really. Reference types have the concept of identity and equality, while value types only have equality. Boxing and unboxing does not change this fundamental difference.

Autoboxing in C# allows value types to go back and forth between being objects, but each time a value type becomes an object it acquires a new identity. Here's a sample program to illustrate this problem:
   using System;

struct A {
}

class B {
}

public class Test {
  static Object Process(Object o) {
    if (o is A) {
       A a = (A)o;
       // do something with a
       return a;
    }
    else if (o is B) {
       B b = (B)o;
       // do something with b
       return b;
    }
    return null;
  }

  static void Check(Object o) {
    Console.WriteLine(o == Process(o));
  }

  public static void Main(string[] args) {
    Check(new A());
    Check(new B());
  }
}
This program prints
   False
True
which may be unexpected if you don't understand that value types and reference types cannot be treated the same way. This illustrates the fact that C# does not bridge the gap between value types and reference types.

The solution to this problem is to use wrapper classes. Pretending there is no difference between reference types and value types is dangerous as it will invariably result in hard-to-detect bugs, as demonstrated by this example.

Q: What other pitfalls should I be aware of?
A: A boxing conversion always makes a copy of the value being boxed. This is different from a conversion of a reference-type to type object, in which the value continues to reference the same instance. For example, given the declaration
   struct Point {
  public int x, y;
  public Point(int x, int y) {
    this.x = x;
    this.y = y;
  }
}
the following statements
   Point p = new Point(10, 10);
object box = p;
p.x = 20;
Console.Write(((Point)box).x);
will output the value 10 because the implicit boxing operation that occurs in the assignment of p to box causes the value of p to be copied. Had Point been declared a class instead, the value 20 would be output because p and box would reference the same instance.

Q: Isn't it cool to have collections which can take both ints as well as strings?
A: Yes, but note that if you store a value type in a collection, then in order to update the value you have to unbox, update the value, rebox the updated value and then replace the object in the collection. This has two problems:

  • Performance. As this article shows, you can almost double performance by using wrapper classes (see IntHolderClass) instead of autoboxing.
  • Loss of identity. The updated object is not the same as the old object. This can break code that depends on the identity of the object. Again, this problem can also be solved by using a wrapper class.

Q: What can we conclude from all this?
A: The 'Type System Unification' in C# is half-baked and full of pitfalls.

C# Best Practice

Do not use boxing and unboxing

[Back to Index]













1