IEquatable<T>.Equals 中的条件

Posted

技术标签:

【中文标题】IEquatable<T>.Equals 中的条件【英文标题】:Condition in IEquatable<T>.Equals 【发布时间】:2011-09-11 22:25:24 【问题描述】:

我已经实现IEquatable&lt;T&gt; 来比较两个列表中的对象,但是我想有条件地这样做:

public bool Equals(CustomerType other)


    if (this.Zipcode == "11111" || this.Zipcode == "22222" || this.Zipcode== "33333")
       
          return this.FirstName.Equals(other.FirstName) && this.LastName.Equals(other.LastName) && this.MiddleName.Equals(other.MiddleName);
       
       else
       
          return this.FirstName.Equals(other.FirstName);
       

这里出了点问题,它永远不会进入 else 状态。谁能告诉我哪里出错了?

--用法--

var v = listA.Except(listB).ToList();

--GetHashCode实现--

    public override int GetHashCode()
    
        unchecked
        

            int hash = 17;         
            hash = hash * 23 + this.intField1.GetHashCode();         
            hash = hash * 23 + this.intField2.GetHashCode();         
            hash = hash * 23 + this.stringField3.GetHashCode();
            hash = hash * 23 + this.doubleField4.GetHashCode();
            hash = hash * 23 + this.doubleField5.GetHashCode();         

            return hash;
        
    

--等于--

    public override bool Equals(object obj) 
    
        if (obj == null) return base.Equals(obj);
        if (obj is CustomerType ) 
         
            return this.Equals((CustomerType)obj); 
         
        else 
         
            return false; 
         
    

--示例--

ListA 有 2 个客户:

客户1: 名字 - “A” 姓氏 - “Z” 中间名 - “Y” 邮政编码 - “11111”

客户2: 名字 - “B” 姓氏 - “X” 中间名 - “W” 邮政编码 - “44444”

ListB 有 2 个客户:

客户1: 名字 - “A” 姓氏 - “Z” 中间名 - “Y” 邮政编码 - “11111”

客户2: 名字 - “B” 姓氏 - “G” 中间名 - “G” 邮政编码 - “44444”

在这里,当我说 ListA.Except(ListB) 时,它会将 CustA 与 FirstName、MiddleName、LastName 进行比较,因为它属于 Zipcode 11111 和只有 FirstName 的 CustB,当我说 ListB.Except(ListA) 时也是如此。当前 Equals 实现中发生的情况是它可以与 ListA.Except(ListB) 一起正常工作,但是当我说 ListB.Except(ListA) 时,它会比较 CustB 的 FirstName、LastName 和 Middlename。

【问题讨论】:

我们能看到实际使用它的代码吗?我怀疑问题出在哪里,但想通过查看您的使用方式来验证。 @Eric Petroelje - 编辑了代码用法 你是否也覆盖了正常的等于? 是的。我做到了。通过实施编辑了问题。 现在它返回包含邮政编码不是 11111 或 22222 或 33333 的客户的列表。它不只比较名字 【参考方案1】:

我很确定您没有正确实现 GetHashCode()。每当您覆盖 Equals 时,您必须覆盖 GetHashCode() 以便它们保持一致。

条件是如果两个对象o1.Equals(o2)返回true,那么GetHashCode的结果必须相同。

由于Except 在内部使用哈希集,因此GetHashCode() 的实现在这里是相关的。如果没有哈希集,它的复杂性将从 O(n) 增加到 O(n^2),这显然是不可取的。

此外,Equals 应该是对称的,而你的不是。


查看您的 GetHashCode() 函数显然是错误的。它考虑了Equals 没有考虑的字段。

当您的代码进入ifthen 部分时,可能需要考虑FirstNameLastNameMiddleName。当您的代码进入else 部分时,它可能只考虑FirstName 来考虑GetHashCode()

public override int GetHashCode()

    unchecked
    
    if (this.Zipcode == "11111" || this.Zipcode == "22222" || this.Zipcode== "33333")
    
        return FirstName.GetHashCode()*529+
               LastName.GetHashCode()*23+
               MiddleName.GetHashCode();
    
    else
    
        return FirstName.GetHashCode();
    

但即使使用 GetHashCode() 的这种实现,您仍然应该修复 Equals 的对称性

【讨论】:

@CodeInChaos - 我已经实现了 GethashCode。让我把实现放在问题细节中 +1 用于提及对称性问题。即使 GetHashCode 始终如一地实施,我怀疑这可能是问题所在。 GetHashCode 中应该包含哪些字段?只有那些我需要比较的? 显然只有你比较的那些。否则,当您有两个在比较字段中相同但在其他散列字段中不同的对象时,就会违反一致性条件。 不知道你所说的“你的 Equals 的对称性”是什么意思。它有什么问题?你能解释一下并建议我应该在那里解决什么吗?【参考方案2】:

关于你的equals和hashcode函数的对称性,根据你在另一个答案的cmets中所说的,我相信这是你需要的实现:

public bool Equals(CustomerType other)


    if ((this.Zipcode == "11111" || this.Zipcode == "22222" || this.Zipcode== "33333") &&
        (other.Zipcode == "11111" || other.Zipcode == "22222" || other.Zipcode== "33333"))
       
          return this.FirstName.Equals(other.FirstName) && 
                 this.LastName.Equals(other.LastName) && 
                 this.MiddleName.Equals(other.MiddleName);
       
       else
       
          return this.FirstName.Equals(other.FirstName);
       

对于 GetHashCode:

public override int GetHashCode()

    if (this.Zipcode == "11111" || this.Zipcode == "22222" || this.Zipcode== "33333")
    
        return FirstName.GetHashCode() ^ LastName.GetHashCode() ^ MiddleName.GetHashCode();
    
    else
    
        return FirstName.GetHashCode();
    

ETA - 稍微扩展一下 - 您的实现似乎与平等的定义不符。

做一个稍微不同的假设(客户 B 和 A 的名字相同),问自己以下问题:

    客户 A 是否等于客户 B?根据您的代码,答案是否定的。 客户 B 是否等于客户 A?根据您的代码,答案是肯定的。

在比较客户 A 和客户 B 时,您的实施将使用名字、姓氏和中间名。但是在将客户 B 与客户 A 进行比较时,您的实现将只使用名字。这违反了fundamental definition of equality:

对称属性状态:

* For any quantities a and b, if a = b, then b = a.

任何依赖于Equals 方法的内置函数都将假定其实现与相等的定义一致。由于您的实现不是,因此您违反了该假设,因此得到了不一致的结果。

【讨论】:

Xoring GetHashCode 中的多个属性通常是一个坏主意,因为这样哈希在这些属性的交换下是不变的。所以碰撞的可能性更大。在这种情况下可能没有问题,但我通常会避免它。 您应该将null 处理添加到您的Equals @Code - 两者都很好,为了清楚起见省略了 null 处理(因为他没有它,所以认为这在他的情况下是不必要的) 我在 Equals 中有空检查。我只是没有把它放在这里。你的回答很有帮助。现在我明白你们所说的“平等的对称性”是什么意思。抱歉,没有 CS 或工程背景。只是一个问题。如果我将 other.Zipcode 放在等号的首位,它会给我错误的结果。这个顺序重要吗?

以上是关于IEquatable<T>.Equals 中的条件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 [重复] 的类中实现 IEquatable<MyType>

List<T>.Contains 和 T[].Contains 行为不同

codeforces 1029 A. Many Equal Substrings

C#在Dictionary中使用枚举作为键

怀疑如何组织我的课程

STL学习笔记— —无序容器(Unordered Container)