Click here to Skip to main content
15,886,833 members
Articles / Programming Languages / C#

Trivialities About Nullable Types

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
12 Aug 2011CPOL4 min read 12.2K   1  
Trivialities about nullable types

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 cannot 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:

JavaScript
int nonNullableInt = null;
int? nullableInt = null;

Line 1 doesn’t work, because null cannot 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 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 therefore a value type itself. Also note that T is limited to contain structs. Therefore, 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:

JavaScript
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.

JavaScript
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:

JavaScript
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 its generic argument:

JavaScript
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:

JavaScript
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 an 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) KnowledgePlaza
Netherlands Netherlands
Since early 2001 I've been working full time as a software developer and since 2004 I've been working mostly with Microsoft technology.
I started out as a product developer, but after a few years I switched to a project company where my roles ranged from developer up to consultant and from team lead and coach to manager.
Eventually I switched jobs and focused on the consultant part and then I got back to building a product once again. Now I work in a job where I get to do both.

Comments and Discussions

 
-- There are no messages in this forum --