A question that often comes up when we discuss Nullable<T> is about the anti-symmetric property. This property states that if a==b then a>=b and a<=b. If a and b are null then this property is not satisfied in the current design as the result of >= and <= is always false when one of the parameters is null. This may seems surprising, but it can be easily understood when considering if null can be ordered.

We decided that null being less of (or more of) all the other elements in the domain would be an arbitrary decision and as such null cannot be ordered. If it cannot be ordered then the result of the relational operators for null is false as these operators can be defined as:

a<b -> ordered(a,b) && a<b

a<=b -> ordered(a,b) && (a<b || a==b)

It might be argued that for practical reasons it is convenient to have the <= and >= operators return true in case null is on both sides, but this choice has some practical drawbacks. Consider the following code:

void ProcessTransactions(int?[] transactions, int? maxValue) {

foreach(int? t in transactions)

if(t < maxValue) ProcessTransaction(t);

}

This code does what the programmer expects, even when maxValue is null (in which case it won’t process any element). Now let’s suppose that the programmer changes his mind and wants to include maxValue in the processing. He will then change the ‘<’ operator to be ‘<=’.

Under the current design he will obtain what he expects, even in the case maxValue is null. By considering null >= null as true, this function would suddenly start processing all the null transactions, which is not what the programmer intended. The problem here is that people tend to think about the ‘>=’ and ‘<=’ operators as a way to include the limit value in the set of things to be processed. Very rarely, they intend to process the null value as well. Another way to think about it is that: deciding that null cannot be ordered implies that >= has to return false to be conceptually consistent and to prevent these quite subtle bugs to occur.