Nullable Conversion

25 Jun 2015

.NET has a static class called System.Convert which allows you to dynamically change the type of an object. The only problem with the API is that it takes and returns an object which you have to cast, so I tried to write a method that would convert one type to another using reflection.

public static T ChangeType<T>(object obj)
{
    return (T)System.Convert.ChangeType(obj, typeof (T));
}

The code is simple enough and it allows for some relatively easy conversions from one type to another, but I ran into a big problem when using it. C# has Nullable types which allow you to represent a possibly null integer for instance.

var five = (int?)5;
var nothing = (int?)null;

It seemed simple enough to use them, but when I tried the following, it failed spectacularly with an InvalidCastException:

var three = ChangeType<int?>(3);

Apparently I am not able to cast from Int32 to Nullable<Int32>, but I had just done it a couple of lines before, so I decided to dig deeper to figure out what exactly had not worked. I fired up LINQPad and used the IL view to compare the code of ChangeType to what I had seen with the earlier cast to int?.

The cast to int? looked like this:

IL_0001:  ldloca.s   00  // get the address of the local "five"
IL_0003:  ldc.i4.5       // load the Int32 constant "5"
IL_0004:  call       System.Nullable<System.Int32>..ctor

When I saw this I realized that when we're casting to a nullable type, we're not doing a regular cast operation, it's a cast operator added for convenience. To emulate this cast in my method, I need a new version of ChangeType that detects a conversion to a nullable type and constructs a new nullable instance in that case. To do that, I have to use reflection to get the constructor for the nullable type and call it.

public static object ChangeTypeEx(object obj, Type type)
{
    if (type.IsGenericType &&
        type.GetGenericTypeDefinition() == typeof (Nullable<>))
    {
        // get the T in ?T
        var typeArgument = type.GetGenericArguments()[0];
        obj = System.Convert.ChangeType(obj, typeArgument);
        // get the Nullable<T>(T) constructor
        var ctor = (T)type.GetConstructor(new[] {typeArgument});
        return ctor.Invoke(new[] {obj});
    }
    return System.Convert.ChangeType(obj, type);
}

While this code is certainly a niche use-case and one may argue that inserting the cast yourself would be better, it allows for innovation in dynamically typed scenarios like loading nullable data from an IDataReader.

public static T GetNullableFieldValue<T>(
    this IDataRecord reader,
    int ordinal,
    T fallback = default (T))
{
    if (reader.IsDBNull(ordinal))
    {
        return fallback;
    }
    return ChangeType<T>(reader.GetValue(ordinal));
}

public static T GetNullableFieldValue<T>(
    this IDataRecord record,
    string column,
    T fallback = default (T))
{
    var ordinal = record.GetOrdinal(column);
    return record.GetNullableFieldValue<T>(ordinal, fallback);
}

Now this method can handle data in a variety of formats:

// get the id column with a default of 0 if id is NULL
var id = reader.GetNullableFieldValue<int>("id", 0);

// get the quantity but fall back to (int?)null if it's NULL
var quantity = reader.GetNullableFieldValue<int?>("quantity");

// it even works with object types
var name = reader.GetNullableFieldValue<string>("name", "UNNAMED");