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 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
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.
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 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
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.