The whole situation is vexing. C# has too many ways to express equality and inequality:
- the == != > < >= <= operators (which are logically static methods)
- the Equals static method (which calls the virtual method), the Equals virtual method, the ReferenceEquals method
- The IComparable and IEquatable interfaces
They all have subtly different semantics and with the exception of static Equals, none automatically uses the other, and none actually has the behavior that I want. Static methods are dispatched based on the compile-time type of both operands; the virtual methods / interface methods are dispatched based on the run-time type of one of the operands, which makes the operation asymmetric; the type of one side matters more than the type of the other.
I can't imagine that anyone thinks that the situation we're in is great; given no constraints, this is not what would have evolved. But managed language designers do have constraints: the CLR does not implement static methods in interface contracts or double-virtual dispatch, or the ability to put an operator constraint on a generic type parameter. And therefore multiple solutions have evolved to solve the equality/inequality problem.
I think that were the CLR and C# designers to go back in time and tell their past selves what features ought to be in v1 of the CLR, some form of static methods in interfaces would be high on the list. If there were static methods in interface then we can define:
interface IComparable<in T, in U>
{
static bool operator <(T t, U u);
static bool operator >(T t, U u);
... etc
And then if you have:
static void Sort<T>(T[] array) where T : IComparable<T, T>
You could then use the <
and ==
and so on operators to compare elements.
Compare(A, B)
, you callA.CompareTo(B)
. And that means it cannot ever work ifA
isnull
, which you might want to support with an overloaded operator (x == null
andnull == x
should check the same thing). I'm not at all convinced this is the primary reason, though. – Encephalo