Friday, 20 April, 2007

When Microsoft released .NET 2.0, they introduced a new feature in to the framework called "nullable types." Of course, reference types have always been nullable. What they meant is that it was now possible to set value types to null if you want to.

The way they did this was by using generics to create a "Nullable" class. This class could be instantiated with any value type and had a handy "HasValue" property which allows you to check whether or not the value is set to null or not.

On top of this, they created a short-hand way to specify the usage of this class in the form of a question mark. This question mark is appended to the value type, like "int?", and when the program is compiled it translates it to "Nullable<int>"

This is a nice little innovation and it has been nice to use this at work. Thank-you Microsoft! Now, In a similar vein, want to show you an innovation of my own that I consider to be far more useful.

One of the most common bugs that affects our code-base at work is the null reference exception. Anybody who has worked on a sizable C# project has seen their fair share of this type of bugs. The normal way to prevent these types of errors is to guard your public methods with checks for null and throw an exception if a null is passed. In fact, you saw this pattern in my last post.

public static Account FindByMobile(string mobilePhoneNumber)
{
	if (mobilePhoneNumber == null)
	{
		throw new ArgumentNullException("mobilePhoneNumber");
	}

	Account accountFound = null;

	for(int i=0; i<Account.AllAccounts.Count; i++)
	{
		if (Account.AllAccounts[i].MobilePhoneNumber == mobilePhoneNumber)
		{
			accountFound = Account.AllAccounts[i];
			i=Account.AllAccount.Count;
		}
	}	
	
	return accountFound;	
}

This approach works okay but it's not great. For a start, this check occurs at run-time. Consequentially, it doesn't prevent the bugs but simply makes any bug of this type easier to find. Implementing this pattern makes the interface more rigid (and therefore higher quality) but does nothing to root out the reason for the check.

It'd be much better if we could prevent these kinds of mistake at compile time. Unfortunately, the static analysis performed at compile time in C# is weak. Real static analysis at compile time is only done by languages such as Eiffel and even in this language it is not very comprehensive.

However, there is one type of static analysis that is well implemented in C#: Type-safety. In C#, if you abuse the type system it won't even let you compile the program. So my next question was this, Can we use the type system to ban null references being passed as parameters in to methods? Well, I wouldn't be writing this post if it wasn't possible so let's cut to the chase.

The principle is very similar to the concept of a Nullable type shown above. We create a generic value type that wraps the type we want to define as "NotNullable". We choose a value type for the wrapper so that the wrapper itself can't be null. Then we make the constructor take the reference type as a parameter. If this parameter is null we throw an ArgumentNullException, if not we assign it to the Value property of the wrapper.

The code for the struct is given below:

public struct NotNullable<T>
{
	private T _value;

	public T Value
	{
		return _value;
	}

	public NotNullable(T item)
	{
		if (item == null)
		{
			throw new ArgumentNullException("item");
		}
	}
}

By using this class in the snippet above, we can now totally remove three lines of code, since it is now impossible for mobilePhoneNumber to be null:

public static Account FindByMobile(NotNullable<string> mobilePhoneNumber)
{
	Account accountFound = null;

	for(int i=0; i<Account.AllAccounts.Count; i++)
	{
		if (Account.AllAccounts[i].MobilePhoneNumber == mobilePhoneNumber.Value)
		{
			accountFound = Account.AllAccounts[i];
			i=Account.AllAccount.Count;
		}
	}	
	
	return accountFound;	
}

This pattern has the potential to remove a large amount of superfluous code from any of your projects and it makes the pre-conditions of your methods clearer. The same is also true for what you're returning. If you never ever return a null from a method then you can use this pattern to communicate that to the caller.

In many methods, you will have to check more than one variable and to check each parameter against null can add up to quite a bit of code. By using this technique you are reducing the cyclometric complexity of the method. This cuts the number of unit tests you have to write, if you want to have decent code coverage. Since high cyclometric complexity is strongly correlated with defects one would also expect your defect count to fall as well.

Percieved objections to the pattern

Q. Haven't you just moved the check for the null out of the method and in to the caller? A caller can still incorrectly initialise NotNullable with a null and cause an exception. It seems that it doesn't really solve the problem.

A. It's correct to say that somebody could try and initialise NotNullable with a null and get an exception. For example, a common pattern at my place of work is that when you attempt to retrieve a record with an id that doesn't exist, the program returns a null instead. It'd be quite easy to imagine this situation:

public static Account FindByMobile(NotNullable<string> mobilePhoneNumber)
{
	Account accountFound = null;

	for(int i=0; i<Account.AllAccounts.Count; i++)
	{
		if (Account.AllAccounts[i].MobilePhoneNumber == mobilePhoneNumber.Value)
		{
			accountFound = Account.AllAccounts[i];
			i=Account.AllAccount.Count;
		}
	}	
	
	return accountFound;	
}

public static void PrintNameAndJobTitle(NotNullable<Account> account)
{
	Console.WriteLine("First Name: {0}", account.Value.FirstName);
	Console.WriteLine("Last Name: {0}", account.Value.LastName);
	Console.WriteLine("Job Title: {0}", account.Value.JobTitle);
}

public static void main()
{
	Account account = FindAccountWithNumber("9999999"); // Doesn't exist. 

	PrintNameAndJobTitle(new NotNullable<Account>(account)); // Exception thrown here.
	
}

But wait a second? If we're using this structure consistently across a piece of software doesn't the fact that the FindByMobile account returns an instance of Account imply that it is in fact nullable? In fact, it should be screaming to you that you need a null check. Therefore, I don't buy this argument - I think that the fact the both the presence and absence of the wrapper communicates information helps to reduce these types of errors.

Let's deal with the second part of the objection. Have we really just moved the null reference check from the method in to the caller? No, we haven't - we've actually prevented us having to check for a null twice. Consider the following snippets that fixes the above bug. The first uses the NotNullable struct the second uses the traditional way:

public static Account FindByMobile(NotNullable<string> mobilePhoneNumber)
{
	Account accountFound = null;

	for(int i=0; i<Account.AllAccounts.Count; i++)
	{
		if (Account.AllAccounts[i].MobilePhoneNumber == mobilePhoneNumber.Value)
		{
			accountFound = Account.AllAccounts[i];
			i=Account.AllAccount.Count;
		}
	}	
	
	return accountFound;	
}

public static void PrintNameAndJobTitle(NotNullable<Account> account)
{
	Console.WriteLine("First Name: {0}", account.Value.FirstName);
	Console.WriteLine("Last Name: {0}", account.Value.LastName);
	Console.WriteLine("Job Title: {0}", account.Value.JobTitle);
}

public static void main()
{
	Account account = FindAccountWithNumber("9999999");  // Doesn't exist. 
	
	if (account == null)
	{
		Console.WriteLine("No account with the mobile number provided exists.");
		return;
	}

	PrintNameAndJobTitle(new NotNullable<Account>(account));
	
}

And now the traditional way:

public static Account FindByMobile(string mobilePhoneNumber)
{
	if (mobilePhoneNumber == null)
	{
		throw new ArgumentNullException("mobilePhoneNumber");
	}
	
	Account accountFound = null;

	for(int i=0; i<Account.AllAccounts.Count; i++)
	{
		if (Account.AllAccounts[i].MobilePhoneNumber == mobilePhoneNumber.Value)
		{
			accountFound = Account.AllAccounts[i];
			i=Account.AllAccount.Count;
		}
	}	
	
	return accountFound;	
}

public static void PrintNameAndJobTitle(Account account)
{
	Console.WriteLine("First Name: {0}", account.Value.FirstName);
	Console.WriteLine("Last Name: {0}", account.Value.LastName);
	Console.WriteLine("Job Title: {0}", account.Value.JobTitle);
}

public static void main()
{
	
	Account account = FindAccountWithNumber("9999999"); // Doesn't exist. 
	
	if (account == null)
	{
		Console.WriteLine("No account with the mobile number provided exists.");
		return;
	}

	PrintNameAndJobTitle(account);
}

Notice that both checks are neccessary! The FindByMobileMethod is public so it needs the check to guard against bad callers. The main method needs the check to prevent getting an exception when trying to call account. By introducing the NotNullable struct we really have legitmately removed code.

So there we have it. I've introduced a nice way to remove the majority of null reference errors from your projects and reduce the amount of code you have to maintain (and write unit-tests for).

Simon

20:14:29 GMT | #Programming | Permalink
XML View Previous Posts