Well, no, not really according to (my understanding of) C# language specification.
While reading the specification, I was struck by the definition of the relational lifted operators. The idea, roughly, is that if you've defined a relational operator working on, say, (non-nullable) types A and B, then the language automatically gives you a lifted operator working on the (nullable) types A? and B?. The specification says: For the relational operators < > <= >= a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. The lifted form is constructed by adding a single ? modifier to each operand type. The lifted operator produces the value false if one or both operands are null. Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.
Isn't it strange to define the result as false when both operands are null? Ok: unless both operands are non-null we can't really execute any user-defined operator (since they are written for non-nullable types) but throwing a NullReferenceException
couldn't convey this idea well enough? You may think that, in a nullable-type, null really means "I don't know", as it happens in SQL. Indeed, in a SQL expression null is different from any value, including itself. Well, as you may be suspecting, I wouldn't write a blog entry without an interesting twist ;-)
So, here it is: the equality operators are lifted too and, guess what, The lifted operator considers two null values equal, and a null value unequal to any non-null value
.
Ah ah! So null is equal to null after all... yet, it's not "less (greater) than or equal" to itself! Am I the only one finding this definition... aehm unusual?
A simple example:
int? i = null;
Console.WriteLine(i == i); // true
Console.WriteLine(i <= i); // false