I'm not familiar with .NET, but I think the equivalence relation
abstraction is cleaner, the natural object model for which would
probably be via equivalence classes.
interface EquivalenceRelation<E, C> {
C getEquivalenceClass(E element);
}
equals/hashCode on instances of C would stand-in for the respective
methods on E in collections that supported them. The EqualityComparer
interface would be more efficient in cases where instances of C cannot
be obtained directly from instances of E, but equivalence relations
have the benefit of being a long-time universally adopted abstraction.
Tom
PS: I think it was an unfortunate mistake that equals() and hashCode()
were included on Object - equality is never intrinsic. Lots of Java
code bends itself out of shape because the standard collections (at
least those that don't take comparators) don't allow equality to be
specified independently of their elements.