Posts from  November 2013


11.1.2013

Using invariants to avoid null check insanity

Null checks aren't fun, but even worse are the ever ambiguous run-time NullReferenceExceptions we might otherwise receive.

Variant

Take the following code:

public class Family
{
    public List<string> Names;
}

// consumer creating a family
var family = new Family();
family.Names = new[] {"John", "Jane"}.ToList();

// consumer adding a name
family.Names = family.Names ?? new List<string>();
family.Names.Add("Baby");

// consumer searching names
var searchName = "Bob";
var hasSomeoneNamed = family.Names != null && family.Names.Contains(searchName);


  • Note: each // consumer comment delineates a separate example of using the Family type
  • There's a whole extra line of code just to add a family member!
  • Searching isn't a simple query, it's also a preventative null check and clever usage of the short circuiting && operator.
  • This extra noise is especially confusing to novice programmers.

Invariant

Invariant - "never changing"

A few simple changes can make things much simpler for consumers. If we require a list of names upon creation of a family, we can do the following:

public class Family
{
    public readonly List<string> Names;

    public Family(IEnumerable<string> names)
    {
        Names = names.ToList();
    }
}

  • As a consumer I can see that Names is readonly, which means it can't be changed after creation.
  • Seeing that the constructor also requires a list of names, even if I didn't know the implementation details it would be safe to assume that the names list is never going to be null, unless someone is out to troll me!
  • Even without requiring a list of names, what would be the purpose of a Names list that was always null?
  • We could go further by including this invariant in the class description or with CodeContracts and bring in static analysis support to help avoid null checks, but readonly alone is a great start.

Look at the impact on consumers:

// consumer creating a family
var names = new[] { "John", "Jane" };
var family = new Family(names);

// consumer adding a name
family.Names.Add("Baby");

// consumer searching names
var searchName = "Bob";
var hasSomeoneNamed = family.Names.Contains(searchName);

I'd much rather maintain this code!

One step further with Encapsulation

We could also encapsulate the Names list:

public class Family
{
    protected readonly List<string> Names;

    public Family(IEnumerable<string> names)
    {
        Names = names.ToList();
    }

    public void AddName(string name)
    {
        Names.Add(name);
    }

    public bool HasSomeoneNamed(string searchName)
    {
        return Names.Contains(searchName);
    }
}

Now our consumers don't even have to be aware of the fact that Names exists let alone that it might be null:

// consumer creating a family
var names = new[] { "John", "Jane" };
var family = new Family(names);

// consumer adding a name
family.AddName("Baby");

// consumer searching names
var searchName = "Bob";
var hasSomeoneNamed = family.HasSomeoneNamed(searchName);


  • This is suggested by the principles behind the Law of Demeter.
  • All null checking would be confined to the Family type, thus making it simple to see the lack of necessity.

However, I usually don't go this far:

  • This can lead to a lot of boiler plate code, which itself has readability and maintainability concerns, especially when redefining all the list operations on the Family type (AddName/RemoveName/EnumerateNames etc).
  • I prefer to wait to create methods on types until I see benefits of re-use among consumers and can compare that to the purpose of my type in the first place.

I prefer the invariant only approach, giving consumers the guarantee that Names won't be null and letting them take it from there.

Serialization concerns

  • If you are serializing objects to a database or other medium, be aware of how these impact your invariants.
  • readonly can cause a lot of friction in serialization, if it does, try an auto property with a public getter and protected/private setter, but be aware that deserializers may leave this null.
    • protected/private setters guarantee that at least code consumers aren't modifying the field.
  • I strongly recommend a good understanding of your serializer and possibly even some unit/integration tests to verify this invariant.

Conclusion

Null check insanity is often a sign of design smell, invariants are a great first step in the direction of creating a solid contract between producers and consumers of a type.

It may seem like work to enforce invariants, but the dividends in maintainability and readability are worth it. Think how often you stumble upon null checks, or the lack thereof. Understanding these patterns will make them second nature.