繁体   English   中英

IEquatable之间的差异 <T> ,IEqualityComparer <T> ,并在自定义对象集合上使用LINQ时覆盖.Equals()?

[英]Differences between IEquatable<T>, IEqualityComparer<T>, and overriding .Equals() when using LINQ on a custom object collection?

比较自定义对象的两个集合时,使用Linq的.Except()方法遇到一些困难。

我从Object派生了我的类,并实现了Equals()GetHashCode()和运算符==!=重写。 我还创建了一个CompareTo()方法。

在我的两个集合中,作为调试实验,我从每个列表中提取了第一项(重复的项),并将它们进行如下比较:

itemListA[0].Equals(itemListB[0]);     // true
itemListA[0] == itemListB[0];          // true
itemListA[0].CompareTo(itemListB[0]);  // 0

在这三种情况下,结果都是我想要的。 但是,当我使用Linq的Except()方法时, 不会删除重复项:

List<myObject> newList = itemListA.Except(itemListB).ToList();

了解Linq如何进行比较后,我发现了各种(冲突的?)方法,这些方法说我需要从IEquatable<T>IEqualityComparer<T>等继承。

我很困惑,因为当我从IEquatable<T>继承时,我需要提供一个新的Equals()方法,该方法具有与我已经覆盖的方法不同的签名。 我是否需要两个具有不同签名的方法,还是应该不再从Object派生我的类?

我的对象定义(简化)如下所示:

public class MyObject : Object
{
    public string Name {get; set;}
    public DateTime LastUpdate {get; set;}

    public int CompareTo(MyObject other)
    {
        // ...
    }

    public override bool Equals(object obj)
    {
        // allows some tolerance on LastUpdate
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + Name.GetHashCode();
            hash = hash * 23 + LastUpdate.GetHashCode();
            return hash;
        }
    }

    // Overrides for operators
}

我注意到,当我从IEquatable<T>继承时,可以使用IEquatable<MyObject>IEquatable<object> 当我使用另一个时,对Equals()签名的要求会发生变化。 推荐的方法是什么?

我要完成的工作:

我希望能够使用Linq(Distinct / Except)以及标准的相等运算符( ==!= )而无需复制代码。 如果两个对象的名称相同并且 LastUpdate属性在几秒(用户指定)的公差内,则比较应允许将它们视为相等。

编辑:

显示GetHashCode()代码。

不要紧,你是否覆盖object.Equalsobject.GetHashCode ,实现IEquatable ,或提供IEqualityComparer 它们都可以工作,只是方式略有不同。

1)从object覆盖EqualsGetHashCode

从某种意义上说,这是基本情况。 假定您可以编辑类型以确保两种方法的实现均符合要求,那么通常它将正常工作。 在很多情况下,这样做是没有错的。

2)实现IEquatable

这里的关键点是您可以(并且应该)实现IEquatable<YourTypeHere> 此方法与#1之间的主要区别在于,您具有Equals方法的强大类型,而不仅仅是让它使用object 这不仅为程序员提供了更好的便利(增加了类型安全性),还意味着不会将任何值类型装箱,因此可以提高自定义结构的性能。 如果执行此操作,除了#1之外,您几乎应该始终执行此操作,而不是代替。 这里的Equals方法在功能上不同于object.Equals是...不好的。 不要那样做

3)实现IEqualityComparer

这与前两个完全不同。 这里的想法是对象没有得到它自己的哈希码,或者没有看到它是否等于其他东西。 这种方法的重点在于, 对象不知道如何正确获取其哈希值或查看其是否等于其他值 可能是因为您不控制该类型的代码(例如,第三方库)并且他们没有费心重写行为,或者也许他们确实重写了行为,但是您只想在其中自己定义“平等”这种特殊的情况。

在这种情况下,您将创建一个完全独立的“比较器”对象,该对象接受两个不同的对象,并通知您它们是否相等,或者一个对象的哈希码是什么。 使用此解决方案时, EqualsGetHashCode方法对类型本身所做的操作无关紧要 ,您将不会使用它。


请注意,所有这一切与==运算符完全无关,后者是它自己的野兽。

我用于对象相等的基本模式如下。 请注意,只有两种方法具有特定于该对象的实际逻辑。 剩下的只是提供给这两种方法的样板代码

class MyObject : IEquatable<MyObject> { 
  public bool Equals(MyObject other) { 
    if (Object.ReferenceEquals(other, null)) {
      return false;
    }

    // Actual equality logic here
  }

  public override int GetHashCode() { 
    // Actual Hashcode logic here
  }

  public override bool Equals(Object obj) {
    return Equals(obj as MyObject);
  }

  public static bool operator==(MyObject left, MyObject right) { 
    if (Object.ReferenceEquals(left, null)) {
      return Object.ReferenceEquals(right, null);
    }
    return left.Equals(right);
  }

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

如果遵循此模式,则实际上不需要提供自定义IEqualityComparer<MyObject> EqualityComparer<MyObject>.Default就足够了,因为它将依赖IEquatable<MyObject>来执行相等性检查

您不能“允许对LastUpdate有所容忍”,然后使用使用LastUpdate严格值的GetHashCode()实现!

假设this实例在23:13:13.933具有LastUpdate ,而obj实例具有23:13:13.932 然后这两个可以与您的宽容想法相提并论。 但是,如果是这样,则其哈希码必须为相同的数字。 但是除非您非常幸运,否则不会发生这种情况,因为DateTime.GetHashCode()不应两次给出相同的哈希值。

此外,您的Equals方法在数学上最是传递关系。 并且“近似等于”不能被传递。 它的传递式闭合是识别所有事物的琐碎关系。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM