3

In .NET, the fact that IEnumerable<T> extends IEnumerable often comes in handy. Frustratingly, though IEqualityComparer<T> and IComparer<T> do not extend their non-generic counterparts, despite the fact that the EqualityComparer<T> and Comparer<T> classes implement both interfaces. Is there a reason for this discrepancy?

ChaseMedallion
  • 20,860
  • 17
  • 88
  • 152

1 Answers1

0

If I have a sequence of strings I can use that to get a sequence of objects, since each string is also an object. This is true of any sequence; I can always get a sequence of objects when given any sequence.

If I have an object that can compare two strings I can't use it to compare two objects, since those two objects might not be strings.

The reason for this is because IEnumerable<T> is covariant, whereas IComparer and IEqualityComparer are not. (Not just in the C# sense, although that's true as well, but also in the conceptual computer science sense.)

As for why the concrete EqualityComparer<T> and Comparer classes implement both interfaces, rather than not just the generic versions, is a decision that can only really be explained by the employees who choose to create those types. I'd imagine that they did it because at the time enough people were using the non-generic versions of the interfaces that they wanted the types to be usable with all of the existing non-generic code.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • But this doesn't explain why the EqualityComparer class implements both interfaces. For example, the following compiles but throws an exception: IEqualityComparer cmp = EqualityComparer.Default; Console.WriteLine(cmp.Equals(2, 2)); – ChaseMedallion Mar 22 '13 at 18:45
  • 1
    @ChaseMedallion That's because `EqualityComparer.Default` doesn't return an `IEqualityComparer`, it returns an `EqualityComparer`. It's a concrete class, not an interface, and that concrete class implements both interfaces (even though the generic version doesn't inherent from the non-generic one). – Servy Mar 22 '13 at 18:50
  • I understand why it compiles, I'm just wondering why the framework designers would choose to go one way for the class and another for the interface. – ChaseMedallion Mar 22 '13 at 19:03
  • @ChaseMedallion If you implement `IComparer` to compare objects of type `T`, you do not have to include functionality to compare objects that are not type `T`. However, you *can* choose to provide the additional functionality if you wish. The `EqualityComparer` and `Comparer` classes do implement this support, so in addition to implementing the generic interfaces they implement the non-generic ones. – Sam Harwell Mar 22 '13 at 19:11
  • @Servy: The fundamental reason `EqualityComparer` can inherit the non-generic form is that its behavior is that all objects possess `Equals(Object)` and `GetHashCode()` methods, and the purpose of `EqualityComparer` is to implement the same equivalence relation as those methods. Most *other* implementations of `IEqualityComparer`, however, exist to implement some *other* equivalence relation; while it might be possible to implement an `IEqualityComparer` which compares things that aren't T using `GetHashCode` and `Equals` and things that are `T` some other way... – supercat Oct 15 '14 at 00:12
  • ...such split functionality is more likely to be confusing than helpful. – supercat Oct 15 '14 at 00:13