I’m still working to embrace strict null
in C#. In the mean time here are a few notes on how I try to deal with null
today.
Avoid null
as a sentinel value. Sentinel values are really just a specific example of the more general “magic number” anti-pattern. Magic numbers are bad enough in general. At least a magic number like -1
doesn’t tend to accidentally appear. However, magic values like 0
and null
are easily set accidentally.
If you really need to communicate something complicated back to the caller, then return a data structure with better semantics. Languages like Python and Go make this easy because their functions can return multiple values or use tuple unpacking. C# doesn’t have those features, so you will need to define a class or struct to hold the values you need to explain to the caller. Still it is better than using magic numbers to communicate.
Specifically for lists, dictionaries, and other data structures, returning an empty structure is often better than returning null
. Functions can often be chained together in pipelines, and some of the functions in the pipeline might not accept null
. Usually, they will happily accept empty data structures.
A null
value is usually more obviously “blank” than an empty string. Unless an empty zero length string actually has semantic meaning to your context, it’s probably best to just return null
.
Accept null
values whenever possible. This is a more specific case of Postel’s Law: be liberal in what you accept, and conservative in what you send. If somebody passes in null
, treat that as an empty value, as far as you can. Try not to throw an exception.
Certainly, if a null
value is an error in your context, then by all means throw an exception.
In C# you can quickly turn a null
value into a reasonable default by using the ??
operator.
If null
or empty is a reasonable value for your semantics, then go ahead and return it. For example if you have a search function, and the caller searches for something that doesn’t match anything, then returning null
is fine. Especially if they pass in null
, then return null
. SQL makes good use of this principle with its “three-valued logic”. Propagating null
through the system, rather than throwing exceptions, can often make the most sense.