I would claim that Nullable
comparison X==Y
behave exactly as should intuitively be expected, but I sense a possible misunderstanding here regarding X.Equals(Y)
, which behaves as if it is a static
method call.
object A, B;
MyStruct? X, Y;
The quote "Equals applied to any null object returns false." is true for the non-static method A.Equals(B)
, and should typically hold for any sensible overrides of it.
However, the only reason this is so, is because A.Equals
is a non-static method belonging to A
, meaning A
can never be null
. Attempting A.Equals(B)
with A = null
would indeed throw a NullReferenceException
.
The reason this has to be so is that the instance referred to by A
might be of a more specific type than the declared variable A
, and this more specific type might in turn override the A.Equals
method. In other words, if A
is null, then the runtime have no idea which implementation of A.Equals
to use, and must therefore throw an Exception
.
However static methods can't be overridden, meaning A
can be null
because the method to run is known already at compile-time. (This is why calling static methods is marginally faster than calling their non-static counterparts, and also why the compiler may decide to inline some static calls, but I am digressing.)
So, for any static implementations both operands can be null
. Also, null
should always be considered the same as null
, and different from any other value. (If an implementation of Equals
deviates from this common assumption, then that will typically be considered a bug.)
Examples of static Equals
methods:
A==B;
A!=B;
MyClass.Equals(A, B);
object.Equals(A, B);
ReferenceEquals(A, B);
In this regard Nullable
behave the same way as objects. The only difference being that X.Equals(Y)
supports either X or Y or both to be null without throwing an Exception. So it is more correct to say that it behaves like the static equality methods mentioned above, which is typically more desirable.
But how is it possible for X.Equals(Y)
to behave like a static method? This is because Nullable
is a struct. Structs in C# does not support inheritance, so there is no difference between the type of the variable and the type of the instance. Which is why the runtime is never in doubt of which method Equals refers to. And so no Exception is needed. Any value supported by the struct can therefore be supported by the method for both operands, and null
is therefore just another value.
Now, in addition to this there is some built-in special support for syntactical sugar and optimizations for Nullable
types, but the behavior remains, as far as I know, as I described above.