TechQuiz Question 3: Nullable boxing

2012-04-16    

Why does Foo<int?> throw an exception?

static void Foo<T>() where T : new()
{
    T t = new T();

    Console.WriteLine("ToString: " + t.ToString());

    Console.WriteLine("GetHashCode: " + t.GetHashCode());

    Console.WriteLine("Equals: " + t.Equals(t));

    Console.WriteLine("GetType: " + t.GetType());
}

public void Main()
{
    Foo<int>();
    Foo<int?>(); // Exception thrown
}

Answer

int? is a nullable type. T t = new T(); returns a Nullable with a value of null. If we have a look at the definition of Nullable we find the following:

 [Serializable]
 public struct Nullable<T> where T : struct
 {
    public Nullable(T value);
    public static explicit operator T(Nullable<T> value);
    public static implicit operator Nullable<T>(T value);
    public bool HasValue { get; }
    public T Value { get; }
    public override bool Equals(object other);
    public override int GetHashCode();
    public T GetValueOrDefault();
    public T GetValueOrDefault(T defaultValue);
    public override string ToString();
}

As you can see, Nullable overrides ToString, GetHashCode and Equals. Calling these methods on a nullable type is save will give you a nice result.

But GetType is another story. GetType only exists on the base class Object and cannot be overridden. When calling GetType on a Nullable, your item is boxed to Object.

int i = 123;
// The following line boxes i.
object o = i;  

If you have a Nullable with a value of null and you box it to object you get null back. Think of it like having a null value being boxed to a null reference. And calling GetType() on null gives you an exception!