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)?
C# Contravariance Cotravariance allows a method with the parameter of a base class to be assigned to a delegate that expects the parameter of a derived class. Continuing with the example above, add Method3 that has a different parameter type than delegate: Example: Contravariance with Delegte.
A type can be declared contravariant in a generic interface or delegate only if it defines the type of a method's parameters and not of a method's return type. In , ref , and out parameters must be invariant, meaning they are neither covariant nor contravariant.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With