39

我应该同时实现IComparable和泛型IComparable<T>吗?如果我只实现其中一个,是否有任何限制?

4

3 回答 3

24

是的,你应该同时实现这两个。

如果你实现一个,任何依赖于另一个的代码都会失败。

有很多代码使用其中一个IComparableIComparable<T>两个,所以实现两者可以确保你的代码可以使用这样的代码。

于 2011-09-04T18:31:51.247 回答
23

Oded 是正确的,您应该同时实现这两者,因为有集合和其他类仅依赖于其中一种实现。

但是有一个技巧: IComparable<T> 不应该抛出异常,而 IComparable 应该。在实现 IComparable<T> 时,您负责确保 T 的所有实例都可以相互比较。这也包括 null(将 null 视为小于 T 的所有非 null 实例,你会没事的)。

但是,一般 IComparable 接受 System.Object,并且您不能保证所有可能的对象都可以与 T 的实例进行比较。因此,如果您将非 T 实例传递给 IComparable,只需抛出 System.ArgumentException。否则,将调用路由到 IComparable<T> 实现。

这是示例:

public class Piano : IComparable<Piano>, IComparable
{
    public int CompareTo(Piano other) { ... }
    ...
    public int CompareTo(object obj)
    {

        if (obj != null && !(obj is Piano))
            throw new ArgumentException("Object must be of type Piano.");

        return CompareTo(obj as Piano);

    }
}

This example is part of a much longer article which contains extensive analysis of side-effects that you should take care of when implementing IComparable<T>: How to Implement IComparable<T> Interface in Base and Derived Classes

于 2013-05-10T23:15:44.823 回答
4

虽然 IEquatable<T> 通常不应由未密封的类实现,因为这种派生会与继承发生奇怪的关系,除非实现简单地调用 Object.Equals(在这种情况下它将毫无意义),而泛型 IComparable<T 会出现相反的情况>。Object.Equals 和 IEquatable<T> 的语义意味着,无论何时定义 IEquatable<T>,它的行为都应该反映 Object.Equals 的行为(除了可能更快并避免装箱)。两个 DerivedFoo 类型的对象在视为 DerivedFoo 类型时比较相等,在视为 Foo 类型的对象时也应该比较相等,反之亦然。另一方面,完全有可能两个 DerivedFoo 类型的对象在视为类型 DerivedFoo 时排名不等,但在视为类型 Foo 时应该排名相同。

例如,假设一个类 SchedulerEvent 包含字段 ScheduledTime(DateTime 类型)和 ScheduledAction(MethodInvoker 类型)。该类包括子类型 SchedulerEventWithMessage(它添加了一个字符串类型的 Message 字段)和 SchedulerEventWithGong(它添加了一个 Double 类型的 GongVolume 字段)。SchedulerEvent 类具有按 ScheduledTime 的自然排序,但是相对于彼此无序的事件完全有可能不相等。SchedulerEventWithMessage 和 SchedulerEventWithGong 类之间也有自然顺序,但与 SchedulerEvent 类的项目相比则没有。

假设一个有两个 SchedulerEventWithMessage 事件 X 和 Y 同时调度,但是 X.Message 是“aardvark”,Y.Message 是“zymurgy”。((IComparable<SchedulerEvent>)X).CompareTo(Y) 应该报告零(因为事件具有相等的时间)但是 ((IComparable<SchedulerEventWithMessage>)X).CompareTo(Y) 应该返回一个负数(因为“aardvark”排序在“zymurgy”之前)。如果该类没有以这种方式运行,那么对包含 SchedulerEventWithMessage 和 SchedulerEventWithGong 对象混合的列表进行一致的排序将是困难的或不可能的。

顺便说一句,有人可能会争辩说,让 IEquatable<T> 的语义仅根据类型 T 的成员比较对象是有用的,这样例如 IEquatable<SchedulerEvent> 将检查 ScheduledTime 和 ScheduledAction 是否相等,但即使在应用时也是如此到 SchedulerEventWithMessage 或 SchedulerEventWithGong 不会检查 Message 或 GongVolume 属性。事实上,这些对于 IEquatable<T> 方法来说是有用的语义,我更喜欢这样的语义,但是对于一个问题:Comparer<T>.Default.GetHashCode(T) 总是调用相同的函数 Object.GetHashCode() 而不管类型 T。这极大地限制了 IEquatable<T> 使用不同类型 T 改变其行为的能力。

于 2011-09-13T04:29:43.870 回答