Unsealed types should not implement IEquatable<T>
, since the only way to ensure (or even make it likely) that derived types will implement it correctly is to implement IEquatable<T>.Equals(T)
so as to call Object.Equals(Object)
. Since whole purpose of IEquatable<T>
is to avoid wasting CPU time converting things to Object
before comparing them, an implementation which calls Object.Equals(Object)
couldn't do anything faster than the default behavior that would be achieved if the interface wasn't implemented.
Sealed class types may achieve a slight performance advantage by implementing IEquatable<T>
; the preferred style is to have Object.Equals(Object)
try to cast the parameter to T
; if the cast succeeds, use the IEquatable<T>.Equals
implementation; otherwise return false
. In no case should IEquatable.Equals(T)
and Object.Equals(Object)
yield different results when passed the same object instance.
Structure types may use the same pattern as sealed class types. The method of attempting the cast is a little different, since a failed cast can't return null
, but the pattern should still be the same. If a struct implements a mutating interface (as is the case with e.g. List<T>.Enumerator
, a struct that implements IEnumerator<T>
), the proper behavior of equality comparisons is a bit murky.
Note, btw, that IComparable<T>
and IEquatable<T>
should be considered independent of each other. While it will often be the case that when X.CompareTo(Y)
is zero, X.Equals(Y)
will return true, some types may have a natural ordering where two things may be different without either ranking about the other. For example, one might have a NamedThing<T>
type which combines a string
and a T
. Such a type would support a natural ordering on the name, but not necessarily on T
. Two instances whose names match but whose T
's differ should return 0
for CompareTo
, but false
for Equals
. Because of this, overriding IComparable
does not require overriding GetHashCode
if Equals
is not changed.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…