Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Avoiding null in C#

0.00/5 (No votes)
20 Jun 2004 3  
Make Code More Robust by Avoiding nulls in C#

Introduction

The null keyword is a special case for a variable value. Code robustness can be improved by reducing the number of special cases that have to be dealt with. In this article I shall explain how to reduce the number of times you use the null keyword and thus (hopefully) improve the stability of your code.

Background

I come from a mainly C++ background. I C++ you can get so used to having to handle NULL pointers that doing a check (if (MyClass != NULL) ) become second nature. After converting to C#, however, I realized that it was time to mend my ways. Having to check for null every time I want to access a string or class clutters code and makes it more likely that other programmers in the future will end up getting lots of NullReferenceExceptions to deal with.

Strings

With strings there are normally two special cases that have to be checked: null strings and empty strings. Things are generally easier if we can reduce this to just one special case of empty strings.

When defining a string in a class, don't initialise it to null. Instead, initialise it to the constant string.Empty . This means that whenever another programmer accesses this string through a property, they don't have to worry about getting a null value back.

However, you must also cope with the possibility that the user of the object passes a null back in to the property. Therefore you must catch this and convert it to a null .

For Example, the first of the following classes uses null while the second does not:

// A naive, unstable class that uses nulls

public class MyUnstableStringClass
{
  // Private string variable, initialised to null

  private string mMyString = null;
  
  // Simplistic property for accessing and modifying string.

  public string MyString
  {
    get
    {
      return mMyString;
    }
    set
    {
      mMyString = value;
    }
  }
}

// A stable class that doesn't use nulls.

public class MyStableStringClass
{
  // Private string variable, initialised to empty string.

  private string mMyString = string.Empty;
  
  // Public property for accessing and modifying string.

  public string MyString
  {
    get
    {
      return mMyString;
    }
    set
    {
      if (value != null)
      {
        mMyString = value;
      }
      else
      {
        mMyString = string.Empty;
      }
    }
  }    
}
    

OK, so the second class has a few extra lines of code. That's the penalty for writing stable software, but it's not much. The advantage comes when the classes are used:

  MyUnstableStringClass unstable = new MyUnstableStringClass();
  MyStableStringClass stable = new MyStableStringClass();
  
  // Check for empty strings

  if (stable.MyString.Length == 0)
    Console.WriteLine("Stable string is empty");
    
  // THIS WILL THROW AN EXCEPTION (NullReferenceException)

  if (unstable.MyString.Length == 0)
    Console.WriteLine("Unstable string is empty");

  // To prevent crash for unstable

  if ((unstable.MyString != null) && (unstable.MyString.Length == 0))
    Console.WriteLine("Unstable string is empty");

NOTE: To check for empty strings, use the Length property, rather than comparing to the empty string. This is much faster than a string comparison.

You can see here that the MyStableStringClass.MyString property doesn't need a null-check to prevent a crash. This reduction in code required to use the class will more than make up for the additional few lines required within the class property. It will also help other programmers who may well forget to check for null.

Classes

So far, so good. But strings are pretty simplistic; how can we use this for classes?

If you simply ensure that a class is properly initialised and that none of the properties return null references, then we can use the same method as we used for strings in the first section of this article:

// A complex class containing properties that return simple classes

public class MyComplexClass
{
  // Aggregated MyStableStringClass, available as a property

  private MyStableStringClass mInternalClass = new MyStableStringClass();
  
  // Access to the aggregated class

  public MyStableStringClass InternalStringClass
  {
    get
    {
      return mInternalClass;
    }
    set
    {
      // Replace nulls with a new instance

      if (value != null)
      {
        mInternalClass = value;
      }
      else
      {
        // If this class implemented IDisposable,

        // we would call Dispose() here before re-initialising

        mInternalClass = new MyStableStringClass();
      }
    }
  }
}

As you can see, we initialise the aggregated class to a new instance (same as we set the string to be string.Empty in the MyStableStringClass in the first section of this article). In the InternalStringClass property set method, we replace null references with new instances of the class. Now whenever we access the MyComplexClass.InternalStringClass property, we never have to remember to check for null; we can even go straight to MyComplexClass.InternalStringClass.MyString and never have to worry that is might not exist.

Unfortuantely, there is a downside - you end up using more memory with this method. All classes are always instantiated, and this can become an issue in a system with a large number of classes. Fortunately, a more advanced solution has already been made: the NullObject (PDF file) pattern shows us how to create an equivalent of the string.Empty constant for a class.

I won't go into the details of how to implement the NullObject pattern, I'll save that for another, more advanced article. Unless, of course, you feel inspired to write one first ...

Any comments are welcome. Any spelling mistakes are purely the readers reasonability.

History

  • 19/06/2004 -- First release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here