Introduction
I believe that the code you write should express your intended functionality in a manner that reads almost like a story. To achieve this, you need to use a coding style that includes descriptive names for methods, properties, and fields. One area that a lot of people don’t consider is what their code says when they use null
in C# or Nothing
in VB. Used incorrectly, it can result in confusing code and subtle errors.
So let's start our null
using code to English translator and examine some code idioms.
Initializing a Variable to null
This is the simplest of statements:
ISpoon aSpoon = null;
This means “I’m probably going to use a spoon at some point in this class or method, but I haven't decided which one, haven’t been given one, or haven’t made one yet.” Another way of say this is “There is no spoon”.
Notice that the variable was defined in terms of the interface ISpoon
. The reason for this is that there are many different types of spoon (silver, coffee, sugar, serving, wooden, …) and I want to be able to use the most appropriate for a given situation.
Assigning null to a Variable
Take the following code:
ISpoon aSpoon = GetAWoodenSpoonFromTheDrawer();
theCook.StirsSauceWith(aSpoon);
aSpoon = null;
In this situation, you have gotten a spoon and used it. The assignment says “I’m done with this spoon and don’t care about it anymore. I’ll just drop it wherever, and the cleanup crew (the garbage collector in .NET) will pick it up, wash it, and put it back where someone else can use it”.
Returning null from a Method
Methods will typically return either a single value or a collection of values. For the single value returning method, returning null
will mean something different depending on the intent of the method. For example,
ISpoon aSpoon = GetAWoodenSpoonFromTheDrawer();
would mean to me that there wasn’t a wooden spoon in the drawer. Now you will have to check that aSpoon
is not null
before using it and chose a different type of spoon, or get a wooden spoon elsewhere. Personally, I find this type of code detracts from the flow of the code’s story and prefer to throw an exception as the exception can tell you what went wrong. So instead of:
ISpoon aSpoon = GetAWoodenSpoonFromTheDrawer();
if (aSpoon == null)
aSpoon = FindAWoodenSpoonElsewhere();
if (aSpoon != null)
{
...
}
I would write:
ISpoon aSpoon = null;
try
{
ISpoon aSpoon = GetAWoodenSpoonFromTheDrawer();
}
catch (NoSpoonFoundException)
{
try
{
aSpoon = FindAWoodenSpoonElsewhere();
}
catch(NoSpoonFoundException)
{
aSpoon = BuyAWoodenSpoon();
}
}
catch(DirtySpoonException)
{
WashDishes();
aSpoon = GetAWoodenSpoonFromTheDrawer();
}
if (aSpoon != null)
{
...
}
For the case where the method returns a collection of items, as in:
ICollection<ISpoon> mySilverSpoons = GetAllSpoonsThatAre("silver");
returning a null
does not say “There are no spoons”. Rather, it says “I couldn’t create a collection of spoons”. In this case, I would return a collection with no items. As before, errors should throw exceptions. This means that you can safely iterate the collection returned. The usage pattern would look like:
try
{
ICollection<ISpoon> mySilverSpoons = GetAllSpoonsThatAre("silver");
foreach(var spoon in mySilverSpoons)
{
Polish(spoon);
}
}
catch(MissingSilverwareBoxException)
{
CallPolice();
}
catch(NoPolishAvailable)
{
Order("Polish");
Reschedule("Polish Silver Task");
}
Due to time constraints, I’m not going to address null
parameters in this blog.
I hope this has given you something to think about when architecting/designing you applications. Just remember Nothing
(or null
) can hurt you if you don’t think about Nothing
.