The topic of returning nulls, empty objects and empty collections came up today and we'd like to get the opinion of others. We have read the discussion on returning null for object and separately the discussion on returning empty collections and agree with the marked answers for both topics. However, in our case, we have a class that contains a Foo.Item class and a Foo.Items class, which is a collection of Foo.Item objects. If Foo.Item returns null, does Foo.Items collection return null as well or should it return an empty collection?
views:
89answers:
4Your abstract description is not enough. This is (mostly) not a technical issue with a single answer but a design matter.
If your class also has Foo.Widget and Foo.Widgets, the answers for Item and Widget will not necessarily be the same.
But in general, the collection properties should return empty collections, unless there is a good reason not too. And any code consuming those collection should still check for null. The official (library) recommendation is that of a double-safety.
In case of object you should return it as null because that will allow a quick check for nullability. In case of collection you should return empty list. Empty list is very important because consumer of your code would not need to check for nullability. Considering that they will be using foreach
whatever processing they might be doing will be skipped. If on UI they are trying to draw up a table, they will show empty table. Empty collections are the way to go because of all these benefits.
A collection is an object. There are times when an empty object is better than null, and this comes up particularly often with collections, hence the inclination for people to argue in favour of empty collections. There are problems that empty objects can cause, and these come up more often with other types (particularly value-semantic types), hence the inclination for people to argue against empty objects when you don't particularly mention empty collections.
However, at the end of the day, and empty collection is still and empty object.
It's worth considering that the main reason for using empty collections is also exactly the same reason why empty collections should sometimes not be used, namely that we don't want to have to test for null before doing a for-each.
Okay, so far so good, we return an empty collections all the time and foreach code calling them becomes easier to write.
But wait a minute, there's a flaw here. It could be useful for governments; foreach through the new claims for unemployment assistance made in November, and add up how much its going to cost the exchequer that month. Answer: zero! The reason being that since it's currently October, we don't have any new claims from November yet. The correct answer here is not an empty collection, it's null.
And of course, it's precisely the same sort of example people will use with non-collection empty objects.
So, they each have their place. So also does the middle ground; return a null object, but in some cases coalesce it with an empty object on receiving it.
You could start out with a very simple guideline:
Never return a null reference, if you can reasonably avoid it.
Basically, each null reference is a potential invitation for a program bug. All it takes is not checking for null. By not returning null, you almost certainly make life simpler for the caller.
Consequences of this simple rule are:
If the return type is some collection type, then return an empty collection.
If the return type is some scalar type, then you may have to return null references. Some possible alternatives are:
Returning a "zero", or "empty", or "default" object of the particular return type. (Think along the lines of e.g.
EventArgs.Empty
.)You could define something like a
Nullable<T>
generic type that works for value types as well as reference types. (Rationale: Having to access the actual return value via an explicit property.Value
might make it more obvious to the caller that perhaps he should check for.HasValue
first. With normal, non-wrapped scalar types, one's not especially encouraged or reminded to check for null references before using the value.) -- (Think along the lines ofMaybe<'a>
orOption<'a>
types and according pattern matching in functional languages.)Throwing an exception if the null reference would otherwise indicate an error.
Obviously, each of these suggestions carries its own set of advantages and drawbacks with it. For example, you may no longer have to check for nulls, but there will still be some situations where you would have to check whether a collection is empty; or whether an exception was thrown; etc.
So, I'm not saying that avoiding null references will not always automatically make your life easier, per se -- but I believe the guideline is still very useful because it will make you think about null refences, about the alternatives, and whether they would be more appropriate.