【问题标题】:IComparable and Equals()IComparable 和 Equals()
【发布时间】:2009-09-14 12:37:49
【问题描述】:

来自MSDN

实现 IComparable 的类型必须覆盖 Equals。覆盖 Equals 的类型也必须覆盖 GetHashCode;否则,Hashtable 可能无法正常工作。

我不太明白。谁能解释一下。

【问题讨论】:

  • 更多Equals和GetHashCode的关系:stackoverflow.com/questions/371328/…
  • 另请注意,虽然在每种情况下都必须遵守 IComparable、Equals 和 GetHashCode() 的约定,但不一定需要显式实现所有三个,只要结果一致即可达到。这主要意味着具有相同哈希码的两个对象相等,并且在 CompareTo() 上返回 0。
  • @rushinge 我认为这句话是错误的:'主要意味着具有相同哈希码的两个对象相等,并且在 CompareTo() 上返回 0。 .如果对象相等,建议 GetHashCode() 返回相同的值,CompareTo() 返回 0。您可以轻松生成返回相同哈希码的不同/不相等实例:)

标签: c#


【解决方案1】:

IComparable 是一个接口,它定义了实现类的两个实例可以被视为大于、小于或等于彼此。由于您在该接口的方法中定义了相等性,因此您还需要重写 Equals 方法(和相等运算符)以确保两者的结果一致。

public class EqualityTest : IComparable<EqualityTest>
{
      public int Value { get; set; }

      public int CompareTo(EqualityTest other)
      {
           return this.Value.CompareTo(other.Value);
      }
}

在上面的示例中,我实现了 IComparable,但没有覆盖 Equals。如果您使用具有相同值的类的两个单独实例调用 CompareTo,它将说它们是相等的。如果你用相同的两个实例调用 Equals,它会说它们 not 相等,因为它会测试它们是否是同一个对象(Equals 的默认实现)。

两个相等的项应该返回相同的哈希码(用于快速查找用作哈希表中键的项),因此如果您覆盖 Equals,那么您还应该覆盖 GetHashCode()


例如,我刚刚在我的 IDE 中创建了以下类:

public class EqualityTest
{
     public string A { get; set; }
     public string B { get; set; }
}

然后运行 ​​Resharper 有用的“生成平等”功能,说我希望 A 和 B 都影响平等。这是它创建的代码:

    public bool Equals(EqualityTest other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }

        if (ReferenceEquals(this, other))
        {
            return true;
        }

        return Equals(other.A, A) && Equals(other.B, B);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        if (obj.GetType() != typeof(EqualityTest))
        {
            return false;
        }

        return Equals((EqualityTest)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((A != null ? A.GetHashCode() : 0)*397) ^ (B != null ? B.GetHashCode() : 0);
        }
    }

    public static bool operator ==(EqualityTest left, EqualityTest right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(EqualityTest left, EqualityTest right)
    {
        return !Equals(left, right);
    }

因此,如果您要覆盖 Equals,那么您还应该定义以上所有内容以确保一致性,如果您正在实施 IComparable,那么同样适用。

【讨论】:

  • 在我看来,如果一个类型通常有一个明确的排序顺序,但可能存在一组值,它们彼此不等价,但相对于它们没有自然排名对于彼此而言,CompareTo() 返回零是完全正确的,但 Equals() 返回 false(例如,我认为 Decimal 在比较 1.0m1.00m 时应该表现得那样)。在我看来,Equals 应该定义一个相对严格的相等关系,即使在 ==CompareTo 可能定义一个更宽松的情况下也是如此。
【解决方案2】:

IComparable 用于比较两个对象 - 如果它们被认为相等,则 Compare 将返回 0。如果 IComparable.Compare 为两个对象返回零,而 obj1.Equals(obj2) 返回 false,因为这意味着对象相等的两种不同含义。

当一个类重写 Equals 时,它也应该重写 GetHashCode,因为两个相等的对象应该散列到相同的值,并且这个散列应该基于实现相等时使用的字段/属性。

【讨论】:

  • 如果 IComparable.Compare 返回零,这是否真的意味着项目应该被视为相等,或者仅仅是不存在明确的有序关系?假设有一个不可变类 ScheduleEvent,其中包括 DateTime 类型的 EventTime 字段和 MethodInvoker 类型的 EventAction。 ScheduleEvent 对象将具有基于 EventTime 的自然排序,但我认为具有相同 EventTime 但不同委托的 ScheduleEvent 对象应该从 CompareTo 返回 0,但从 Equals 返回 False。这似乎比任何其他行为都干净。
【解决方案3】:

有两种方法可以比较代码中的对象:EqualsGetHashCode

为了在所有情况下正确比较您的对象,当您覆盖 Equals 方法(用于某些比较)时,您还必须覆盖 GetHashCode(用于其余部分)。

如果您覆盖一个而不是另一个,根据您的代码,您可能会得到意想不到的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-21
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2020-01-23
    • 1970-01-01
    相关资源
    最近更新 更多