【问题标题】:IComparable and IComparable<T>IComparable 和 IComparable<T>
【发布时间】:2015-07-21 21:06:52
【问题描述】:

我应该同时实现IComparable 和通用IComparable&lt;T&gt; 吗?如果我只实现其中一个,是否有任何限制?

【问题讨论】:

  • 你只需要为IComparable&lt;T&gt;编写代码。通过将实际比较委托给通用实现,您可以免费获得 IComparable

标签: c# .net icomparable


【解决方案1】:

是的,你应该同时实现。

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

有很多代码使用IComparableIComparable&lt;T&gt;,但不能同时使用两者,因此实现两者可确保您的代码可以与此类代码一起使用。

【讨论】:

  • @David - IComparable 肯定被某些框架代码使用。我相信Array.SortArrayList.Sort 使用它。
  • 好吧,多么令人失望。能够忘记曾经存在的非通用 .net 真是太好了。
  • 是我还是第一句和最后一句有点矛盾?
  • @Henk 我看不出矛盾。
  • 我也看不出有什么矛盾。最后一句话重申,如果两者都没有实现,代码可能会中断。
【解决方案2】:

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

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

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

示例如下:

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);

    }
}

此示例是一篇更长文章的一部分,该文章包含对实施 IComparable 时应注意的副作用的广泛分析How to Implement IComparable<T> Interface in Base and Derived Classes

【讨论】:

    【解决方案3】:

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

    例如,假设有一个类 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)X).CompareTo(Y) 应该报告零(因为事件具有相等的时间)但是 ((IComparable)X).CompareTo(Y) 应该返回一个负数(因为“aardvark”排序在“zymurgy”之前)。如果该类的行为不是这样,则很难或不可能一致地对包含 SchedulerEventWithMessage 和 SchedulerEventWithGong 对象混合的列表进行排序。

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

    【讨论】:

      猜你喜欢
      • 2020-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多