Sometimes things in the world of software are not that hard. They are just poorly documented or documentation is hard to find. This surely applies for some use cases of nullable types. This post shows the use of nullable types in generic methods and how it’s easier than you might think.
An introduction to nullable types in C#
In order to understand the use of nullable types, it is imperative that you understand the difference between a value type and a reference type. For a complete description of these things have a look at chapter 4 of the C# 4.0 Language Specification (included with Visual Studio 2010). In short it comes down to this: value types directly contain their value, while reference types contain a reference to their value. Value types are always either a struct type or an enumeration type. Struct types include things like numeric types.
As value types always directly contain their value, they can not be null. However, in some cases, you might want to have a structure where you can identify if a value type is actually not defined. In lots of cases simply defining 0 as the undefined value for an int, for example. will not suffice. 0 might actually be a meaningful value for an int.
To provide a way of declaring a variable of a value type that can be null, C# introduces the question mark as a suffix to a value type:
int nonNullableInt = null;
int? nullableInt = null;
Line 1 doesn’t work, because null can not be assigned to a value type. Line 2 does work, because of the question mark behind the variable type.
Under the hood of nullable types
So what happens here? In fact the question mark in this context is syntactic sugar for Nullable<T>. So what happens is that nullableInt in the previous example is actually of type Nullable<int>. Because of this the variable now has a property Value, which contains the actual value if one is a available, and it gets HasValue of type bool, which indicates if a value is actually available.
Note that Nullable<T> is defined as a struct and is therefor a value type itself. Also note that T is limited to contain structs. Therefor it is not possible to apply Nullable<T> (or the question mark suffix for that matter) to a reference type.
Nullable types in generic methods
Now let’s get to the point. Let’s say you want to do some generic handling of input. So write a generic method with a signature like the following:
static string GetStringRepresentation<T>(T value)
You can simply call this with both non-nullable and nullable types. Because the compiler infers the type variable T I don’t even have to tell it what type value is explicitly. Let’s say we have the next program.
static void Main(string[] args)
{
int nonNullableInt = 0;
int? nullableInt = null;
Console.WriteLine(GetStringRepresentation(nonNullableInt));
Console.WriteLine(GetStringRepresentation(nullableInt));
Console.WriteLine("IsNullable(int): {0}", IsNullable(typeof(int)));
Console.WriteLine("IsNullable(int?): {0}", IsNullable(typeof(int?)));
Console.ReadLine();
}
The output of line 7 is actually an empty string. This is because the call to value is actually boxed because the call to ToString() is actually a call to object.ToString(). Because nullableInt is in effect null, it will return an empty string.
Obviously there are many reasons why that might not be correct for a nullable int, so let’s find a way to handle nullable types better in our generic method. The first thing to do is to detect if the passed in value is actually a nullable type. To do this we can use the type system:
static bool IsNullable(Type valueType)
{
return valueType.IsGenericType && valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
What this method does is use the type passed in and checks if it’s a generic type. If it isn’t, it can’t be nullable. It needs that check because otherwise our second check on GetGenericTypeDefinition would throw an exception as it would not have a generic type definition to return. Our second check compares the returned generic type definition with the Nullable<> type. Another task we need to accomplish is to get a default value for a nullable type, or, more specifically, get a default value for it’s generic argument:
static object GetDefaultValue(Type nullableType)
{
Type valueType = nullableType.GetGenericArguments()[0];
return Activator.CreateInstance(valueType);
}
This method takes a type and gets the first generic argument. This code assumes that you’ve already made sure this type is actually a nullable type. Normally you could use the default keyword to get a default value from a generic argument by writing default(T). As in this case we don’t have an actual type declaration, but a Type instance, we need a different approach to get a default value. If you’d check out the C# 4.0 reference you’d find out that the default keyword simply calls a default constructor on the type passed in and returns the result. To mimic that behavior, I simply call Activator.CreateInstance with the underlying type to get a default value. Now if we rewrite our GetStringRepresentation method to use the new methods above, we would end up with something like this:
static string GetStringRepresentation<T>(T value)
{
if (IsNullable(typeof(T)) && value == null)
{
return GetDefaultValue(typeof(T)).ToString();
}
return value.ToString();
}
Now, if we call this method with a int? it would return 0 instead of an empty string. For your convenience, I’ve uploaded a sample project here. I hope you’ve found this an interesting tour around nullable types. If you have any questions or comments, please use the comments form below. Thank you for reading.
No comments:
Post a Comment