If you’re about to use a Nullable<bool>, I would consider using an Enum or other data structure instead. It may help you avoid mistakes when the meaning of NULL is not known.
Recently, I mishandled a Nullable<bool>. I won’t go into the details of the mistake, but lets just say it was bad enough to require a full rollback. I began to wonder how practical it is to use in most applications. (In part in an attempt to hold on to my broken pride as a result of the stupid mistake 🙂 )
In this stackoverflow post, the answer by “Lee” provides a good example of when to use a Nullable<bool> type:
Something can be true, false or undefined for many reasons. How would one answer “Is your third child a girl?” if one only has two children? Both true and false are incorrect. Null would be appropriate as saying that the comparison doesn’t apply.”
This seems to make sense, but the more time I spend with it, the answer becomes FALSE in practice instead of NULL. NULL is just too fuzzy.
Before answering this question one would have to assign a very specific meaning to NULL. Does NULL mean there was an error in the calculation? What if there is an error in the calculation before it is determined that there are only two children? (I would also argue that if there are indeed only two children, this could be handled before the question was posed to the program.)
Because we do not know what NULL means and because it is most definitely not TRUE (because, how could it be?) the BEST answer is FALSE.
Also, if this question indeed returns NULL, we’ve introduced a new definition to the domain. What is the scope of this definition?
So it would appear that TRUE or FALSE states represent a certainty regarding an outcome. NULL can be conceived of in at least three different states. For example, the statement “Thomas went to the bar last night.”
- TRUE – Yes, Thomas went to the bar. (likeliest answer if you knew Thomas)
- FALSE – No, Thomas did not go to the bar.
- NULL – Huh?
- Knowable – Something went wrong in the first calculation. E.g. you asked Thomas the question but sneezed while he replied , rendering you deaf momentarily. Simply asking him again will get you the answer.
- Unknowable – The bar burned down and Thomas skipped town. There is no realistic way of getting the answer. (Please don’t poke holes in this, but I know you will)
- Not applicable – See the example above regarding the three children, but, bars and Thomases instead.
What does NULL mean here? Determining what NULL means must be performed on a case by case basis. This is why I believe it is often better to use an Enum or other structure as it more gracefully exposes the intention behind a non-binary return value.
Here is a rough example of what could be done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Xml; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { var thomas = new Thomas(); var nullableBool = thomas.IWentToTheBarLastNight(); Console.WriteLine("The value is {0}", nullableBool.Value); Console.WriteLine("The detail is {0}", nullableBool.BoolWithDetailEnum); Console.ReadLine(); } } public class NullableBool { private Exception _exception = null; private readonly bool _errorSetsValueToFalse; /// <summary> /// /// </summary> /// <param name="defaultValue">Initialize the return value type</param> /// <param name="errorSetsValueToFalse">If this object instances Exception property is ever set to not null during the lifetime of the object, the return value will be false.</param> public NullableBool(bool defaultValue, bool errorSetsValueToFalse) { this.Value = defaultValue; this._errorSetsValueToFalse = errorSetsValueToFalse; } public bool Value { get { return BoolWithDetailEnum == BoolWithDetailEnum.True; } set { if (value) BoolWithDetailEnum = BoolWithDetailEnum.True; else BoolWithDetailEnum = BoolWithDetailEnum.False; } } public BoolWithDetailEnum BoolWithDetailEnum { get; set; } public Exception Exception { get { return _exception; } set { _exception = value; if (value != null) { BoolWithDetailEnum = BoolWithDetailEnum.Error; } } } } public enum BoolWithDetailEnum { False = 0, True = 1, Unknown = 2, Unknowable = 3, NotApplicable = 4, Error = 5 } public class Thomas { public NullableBool IWentToTheBarLastNight() { var nullableBool = new NullableBool(false, true); try { nullableBool.Value = true; //throw new Exception(); } catch(Exception ex) { nullableBool.Exception = ex; } return nullableBool; } } } |
Thanks for reading!!!