Convert a Value to Nullable

I’ve decided to start a new series-like set of articles on highly reusable code snippets. I write a lot of these types of snippets and figure it’s time to share. Kicking off will be the ConvertToNullable function.

Background

I despise boilerplate code in general, but I really hate writing the same code over and over to check if a variable equals some “default” value and acting on that case. That’s why I wrote SafeDataReader (article to come) years ago. SafeDataReader is constructed with a DbDataReader and adds a ton of new Get method that let you easily and safely access fields on the underlying data reader with strong typing.

One of the key benefits of SafeDataReader is that every Get method has an override for supplying a default value. If you use this version of a method, it will return your default value if the field is null (DBNull.Value).

Unfortunately, I wrote SafeDataReader before the much needed Nullable type was introduced to .NET. I also have not taken the time to go back and update it to support nullables. That leads us to the “dilemma” behind today’s code snippet.

The Problem

Have you ever written something like the following?

DateTime? someDate;
DateTime someTemporaryDate = GetSomeDate();
if (someTemporaryDate == DateTime.MinValue) {
  someDate = null;
} else {
  someDate = someTemporaryDate;
}

The GetSomeDate function may be legacy code, but it only returns DateTime (or another value type). However, you write code for the new millennium and want to use nullables for a clearer intent of the unknown value. Regardless, this type of code block is a PITA that bleeds unreadability.

Going back to my SafeDataReader example, here is some code that is nice and tidy until the DateTime value needs converting to a Nullable<DateTime>.

var setting = new Setting {
  Title = reader.GetString("Title"),
  Quantity = reader.GetInt32("Quantity", 1),
  BeginDate = reader.GetDateTime("BeginDate"),
  EndDate = (reader.IsDBNull("EndDate")
      ? (DateTime?)null
      : reader.GetDateTime("EndDate"))
};

Ugh! I hate that bit with the EndDate. Since Setting.EndDate is a nullable DateTime, I have to jump through ugly hoops to handle a null just because my GetDateTime method doesn’t yet support nullables.

The Reusable Solution

Nothing beats a generic reusable solution to such a problem. Let’s see it!

protected static T? ConvertToNullable<T>(T value, T defaultValue)
  where T : struct {
  if (value.Equals(defaultValue)) {
    return null;
  }
  return value;
}

protected static T? ConvertToNullable<T>(T value) where T : struct {
  return ConvertToNullable(value, default(T));
}

Now, we have something that will clean-up those previous examples.

DateTime? someDate = ConvertToNullable(GetSomeDate());
var setting = new Setting {
  Title = reader.GetString("Title"),
  Quantity = reader.GetInt32("Quantity", 1),
  BeginDate = reader.GetDateTime("BeginDate"),
  EndDate = ConvertToNullable((reader.GetDateTime("EndDate"))
};

That is much better! Enjoy!