The best way to avoid checking all the time whether an object is consistent is to never allow it to get inconsistent.
I’ll try to illustrate the problem with a simple example. The following class expects both the Name and Address fields/properties to actually contain values – not just being null. This is illustrated by the ToString() method, but would in a true scenario happen in most methods of the class. (By the way, note the cool syntactically sweet one-liner for defining fields and properties.)
class Person {
public FullName Name { get; set; }
public Address Address { get; set; }
public Person() {
// Not really doing anything. Just put it here in the
// code sample to show that there is no fancy initialization going on.
}
public override string ToString() {
return String.Format("Person (Name={0}, Street={1}, City={2})", Name.FullName, Address.Street, Address.City);
}
}
It gives quite a lot of responsibility to the user of the class, who is entrusted to assign the fields after having invoked the constructor. Failing to do so will at worst give null reference errors when code depending on the contents of a member field tries to use it. Adding assertions or error handling when a field is about to be used would improve the situation somewhat, while cluttering the code. In the example above, one workaround could be to introduce null-checks in the ToString() method to ensure that a proper “Name missing” or “Null” message is shown, rather than NullReferenceException being thrown. Or, perhaps use String.Format which would be a good idea for formatting the string anyways.
However, there are still several problems with this design:
- Adding new member fields is risky. It can be very tricky to keep track of all the places where the Person class is used. How can you be sure that the new field is added correctly wherever Person instances are created? If it is just an internal class it might be manageble. If not, lets just hope that a unit test will catch it.
- Even though the object is initialized correctly, there is no guarantee that it will still be consistent. What would happen if someone assigns Null to the Name property?
- Adding null checks and proper handling will definitely mess up the code.
- Other problems as well, but mentioning them would move focus away from the point I’m trying to make…
If the object is guaranteed to be consistent when it is created, and impossible to bring into an inconsistent state, there should be no need to have null-checks scattered all over your class. Check the values at the gates – that would be in the constructor and in the setter properties – and you can trust them to be correct afterwards. If the object is immutable it is of course sufficient to only do the check during construction. Use the readonly (C#) or final (java) keyword to get support from the compiler enforcing this, and to state your intentions.
Actually, immutability is a great design concept that should be used more. Eric Lippert has written some great posts on this topic that I recommend! He starts out with Immutability in C# Part 1: Kinds of immutability defining what it all is about, and then goes on with 10 more articles exploring how various common data structures can be given immutable implementations.
I’ll round of this post with a modified version of the class that guarantees that name and address can never be null. If the name and address objects are immutable, then this object will also be truly immutable.
sealed class Person {
readonly FullName name;
readonly Address address;
public FullName Name {
get {
return name;
}
}
public Address Address {/snip/}
public Person(FullName name, Address address) {
if (name == null) throw new ArgumentNullException("name");
if (address == null) throw new ArgumentNullException("address");
this.name = name;
this.address = address;
}
public override string ToString() {
return String.Format("Person (Name={0}, Street={1}, City={2})", Name.FullName, Address.Street, Address.City);
}
}