Partager via


IComaprer & IComparable Refactoring Proposal

We are exploring the possiblility of changing the design of IComparer<T> and IComparable<T> interfaces that will ship with Whidbey. This post describes some more detail on the issues we are trying to address with the design change and we are looking for feedback on the proposal.

Background

The first version of the .NET Framework had three main comparison related-interfaces: IComparable, IComparer, and IHashCodeProvider. One of the main issues with the interfaces was that there was no single interface that could be used by Hashtable to override the default identity of the keys (change which keys compare to equal and which not). You needed to use IHashCodeProvider to provide custom hash codes for the keys and IComparer to provide custom equality (via Compare==0). This resulted in a common usage mistake (a bug) where a user would pass incompatible pair of IHashCodeProvider and IComparer to the Hashtable constructor. For example, a case sensitive hash code provider and a case insensitive comparer. This would result in a bug where two strings that do compare equal could have different hash codes, and so the Hashtable indexer could not find the item with the given key.

We tried to fix this problem in Whidbey generic APIa and decided to put all comparison related methods from IComarer and IHashCodeProvider into a single interface (IComparer<T>), so users could not pass two incompatible implementations of the interfaces to the Hashtable constructor. We have always seen the new design as a compromise between a very clean OO design and trying to avoid explosion of the number of interfaces.

Well, we have received feedback from people using the new interfaces (IComparer<T> and IComparable<T>) that made us thinking that may be we compromised a bit too much J Here are the relevant links describing the issues:

We decided to give it one more shot to fix the concerns. You can find the proposal for the new design below. I think the change proposal addresses most of the concerns, but at the same time it has the following drawbacks:

  • More interfaces to understand. Makes the Framework less approachable.
  • Custom comparers can implement one interface (let’s say IEqualityProvider<T>) but not the other (IComparer<T>). Switching from one collection which uses IEqualityProvider<T> to another which happens to use IComparer<T> may be difficult (as you will need a different custom comparer).

 

I am looking for feedback on the proposal from people who have experimented with the interfaces. The change is something that we would do only if there is an overwhelming positive feedback from the community, not if the improvement seems only marginal.

Thanks!

 

Current Design

public interface IComparable<T> {

    bool Equals(T other);

    int CompareTo(T other);

}

 

public interface IComparer<T> {

    bool Equals(T x, T y);

int Compare(T x, T y);

int GetHashCode(T obj);

}

 

public interface IKeyComparer : IComparer, IHashCodeProvider {

    bool Equals(object x, object y);

}

Proposed New Design

IComparable<T>

· Break into two interfaces

public interface IEquateable<T> {

bool Equals(T other);

}

 

public interface IComparable<T> {

    int CompareTo(T other);

}

IComparer<T>

· Break into two interfaces

public interface IEqualityProvider<T> {

    bool Equals(T x, T y);

int GetHashCode(T obj);

}

 

public interface IComparer<T> {

int Compare(T x, T y);

}

IKeyComparer

· Rename from IKeyComparer to IHashComparer

· Remove IComparer requirement (base type)

public interface IEqualityProvider : IHashCodeProvider {

    bool Equals(object x, object y);

}

Comments

  • Anonymous
    October 27, 2004
    Yes. This is a very good idea.
  • Anonymous
    October 27, 2004
    The comment has been removed
  • Anonymous
    October 28, 2004
    The comment has been removed
  • Anonymous
    October 28, 2004
    Yes this is good. Something that is equatable isn't always comparable. A complex number needs Equals(Complex), but there's little need for CompareTo(Complex).

    What's worse, having equality/comparison on the same interface implied that they did the same thing (Equals==true when Compare==0), and you guys went so far as to say they had inter-related contracts that enforced the same thing.

    But this contract was broken right out of the gate with String, so I think equality needs to be separated from comparison.

    Also, there are MANY times when I want a class's sorting (comparison) to have nothing to do with equality. I may have a Customer object, and I want this to use reference equality (every instance is only equal to itself), but it'd be nice if a Customer would sort by its LastName property by default. Under the current interfaces and "inter-related contract," this is difficult to do and still feel good about myself at the end of the day. If I can implement IComparable<Customer> but not IEquatable<Customer>, falling back on reference quality and hashing (boxing would not be an issue in this case), then I feel good about my design decisions.
  • Anonymous
    October 28, 2004
    Yes. Do the split!

    (But since i opened the MSDN feedback thread mentioned, i'll guess you already know what i feel about this topic...)
  • Anonymous
    October 28, 2004
    BTW - your stylesheet works badly in Firefox. When I mouse over any text on the page, the text dissapears.
  • Anonymous
    October 28, 2004
    The comment has been removed
  • Anonymous
    October 28, 2004
    Sorry again... I found the exact culprit. Your anchor for the comments is '<a name =" feedback" />' which should have a full '</a>' rather than shorthand since you're serving HTML and not XHTML. This is causing all of the comments to be inside an "a", and since you are styling a:hover instead of a:link:hover, everything goes screwball in Gecko.
  • Anonymous
    October 28, 2004
    The comment has been removed
  • Anonymous
    October 28, 2004
    The comment has been removed
  • Anonymous
    October 28, 2004
    The comment has been removed
  • Anonymous
    October 28, 2004
    I totally like this change.
  • Anonymous
    October 28, 2004
    I'm in favor of the proposed new design.
  • Anonymous
    October 29, 2004
    I like the new proposed design.
  • Anonymous
    October 29, 2004
    Go for it! It's better to fix something like this now rather than after a lot of code is written that could have benefited from the change. Thanks for asking!
  • Anonymous
    October 29, 2004
    I'll add my Me Too to the stack.

    I also agree with Joe that you should take the extra e out of IEquateable so that it is just IEquatable, the extra e just looks wrong.

    I also agree that if you're going to have IEqualityProvider, the name for IComparer should be consistent. IOrderingProvider is consistent, and furthermore, is more intention revealing. If you think about it, "comparing" could be either looking at equality or ordering, which is where the original confusion came from.
  • Anonymous
    October 29, 2004
    The comment has been removed
  • Anonymous
    October 29, 2004
    The comment has been removed
  • Anonymous
    October 29, 2004
    The comment has been removed
  • Anonymous
    October 30, 2004
    The comment has been removed
  • Anonymous
    November 01, 2004
    The comment has been removed
  • Anonymous
    November 23, 2004
    I think I'd prefer the way it is now.
  • Anonymous
    July 16, 2006
    PingBack from http://notgartner.com/posts/2969.aspx