20

IEquatable<T> could have been declared to be contravariant in T, since it only uses T in an input position (or, equivalently, U being a subtype of T should imply that IEquatable<T> is [a subtype of] IEquatable<U>).

So, why did the BCL team not annotate it (for C# 4.0) with the 'in' keyword, as they did with many other generic interfaces (like the entirely analogous IComparable)?

Will Dean
  • 39,055
  • 11
  • 90
  • 118
Khalid Khan
  • 203
  • 1
  • 3
  • The problems mentioned in the thread [Should `IEquatable`, `IComparable` be implemented on non-`sealed` classes?](http://stackoverflow.com/questions/1868316/should-iequatablet-icomparablet-be-implemented-on-non-sealed-classes) would just become much worse if `IEquatable<>` was contravariant. – Jeppe Stig Nielsen Jul 09 '13 at 22:32

2 Answers2

14

I think this is mainly for a philosophical reason rather than a technical limitation–as it's perfectly possible to simply annotate the interface. IEquatable<T> is meant to compare objects of the same type for exact equality. An instance of a superclass is not usually considered equal to an instance of a subclass. Equality in this sense implies type equality too. This is a bit different from IComparable<in T>. It can be sensible to define a relative sort order across different types.

To quote MSDN page on IEquatable<T>:

Notes to Implementers:

Replace the type parameter of the IEquatable<T> interface with the type that is implementing this interface.

This sentence further demonstrates the fact that IEquatable<T> is meant to work between instances of a single concrete type.

Community
  • 1
  • 1
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • Thanks; I suspect this is close to the truth. It seems a shame, though, as U being a subtype of T really does mean that IEquatable T is equatable to U... it's just not IEquatable to U! In other words, I don't see any downside to annotating the interface... – Khalid Khan Jul 20 '10 at 11:57
  • I honestly don't see any *upside* to making it contravariant. It wouldn't make sense to say that an instance of, say, `Vehicle` is exactly equal to -- effectively identical to -- an instance of `Car`. They could be equivalent for sort order (as with IComparable), but they would never be really "equal". – Joe White Jul 20 '10 at 12:04
  • 1
    @Khalid: Considering that an ideal base type should not be aware of details of its derived types, it's not in a position to decide equality of those types. A derived class may add some properties, thus diverging from the equality definition that base class provides. – Mehrdad Afshari Jul 20 '10 at 12:05
  • Your valid point about implementation applies even without variance, since one can always pass a subtype to the Equals method at runtime anyway, just because of polymorphism. As things are (without the contravariant annotation) the compiler does not let you express the runtime fact that IEquatable unavoidably acts like IEquatable whenever U is a subtype of T (because of polymorphism, input-only parameters are unavoidably logically contravariant). This inconsistency has arisen because it's up to the BCL team to manually declare the truth about variance on a case-by-case basis... – Khalid Khan Jul 20 '10 at 15:14
2

Inheritable types should generally not implement IEquatable<T>. If IEquatable<T> included a GetHashCode() method, one could define the semantics of IEquatable<T> to say that items should compare equal when examined as T's. Unfortunately, the fact that IEquatable<T> is bound to the same hash code as Object.Equals means that in general IEquatable<T> has to implement essentially the same semantics as Object.Equals.

Consequently, if an implementation of IEquatable<BaseClass> does anything other than call Object.Equals within it, a derived class which overrides Object.Equals and GetHashCode() and does not re-implement IEquatable<BaseClass> will end up with a broken implementation of that interface; an implementation of IEquatable<BaseClass> which simply calls Object.Equals will work just fine, even in that scenario, but will offer no real advantage over a class which doesn't implement IEquatable<T>.

Given that inheritable classes shouldn't be implementing IEquatable<T> in the first place, the notion of covariance is not relevant to proper implementations of the interface.

supercat
  • 77,689
  • 9
  • 166
  • 211