简体   繁体   中英

Condition in IEquatable<T>.Equals

I have implemented IEquatable<T> to compare objects in two lists however i want to do it conditionally like this:

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

Something is wrong here and it never goes to else condition. Can someone please tell me where am i going wrong?

--Usage--

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

--GetHashCode implementation--

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

--Equals--

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

--Example--

ListA has 2 Customers:

Cust1: FirstName - "A" LastName - "Z" MiddleName - "Y" ZipCode - "11111"

Cust2: FirstName - "B" LastName - "X" MiddleName - "W" ZipCode - "44444"

ListB has 2 Customers:

Cust1: FirstName - "A" LastName - "Z" MiddleName - "Y" ZipCode - "11111"

Cust2: FirstName - "B" LastName - "G" MiddleName - "G" ZipCode - "44444"

Here when i say ListA.Except(ListB), it shud compare CustA with FirstName, MiddleName, LastName because it belongs to Zipcode 11111 and CustB with only FirstName and the same applies when i say ListB.Except(ListA). What is happeing in current Equals implementation is that it works fine with ListA.Except(ListB) but when i say ListB.Except(ListA), it compares FirstName, LastName and Middlename for CustB.

I'm pretty sure you did't implement GetHashCode() correctly. Whenever you override Equals you must override GetHashCode() so they are consistent.

The condition is that if for two objects o1.Equals(o2) returns true then the results of GetHashCode must be the same.

Since Except uses a hashset internally the implementation of GetHashCode() is relevant here. Without a hashset it's complexity would grow from O(n) to O(n^2) which is clearly undesirable.

In addition to that Equals should be symmetrical and yours isn't.


Looking into your GetHashCode() function it is clearly wrong. It takes fields into account that Equals doesn't.

Whenever your code goes into the then part of the if it may take FirstName , LastName and MiddleName into account. When your code goes into the else part it may only take FirstName into account for 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();
    }
}

But even with this implementation of GetHashCode() you'll still should fix the symmetry of your Equals

Regarding the symmetry of your equals and hashcode functions and based on what you said in the comments on another answer, I believe this is the implementation you need:

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

And for 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 - to expand a bit - it appears your implementation simply isn't consistent with the definition of equality.

Making a slightly different assumption (that customer B and A have the same first name), ask yourself the following questions:

  1. Is Customer A equal to Customer B? By your code, the answer is no.
  2. Is Customer B equal to Customer A? By your code, the answer is yes.

When comparing Customer A to Customer B, your implementation will use first, last & middle names. But when comparing Customer B to Customer A, your implementation will only use first name. This violates the fundamental definition of equality :

The symmetric property states:

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

Any built in functions that rely on your Equals method will assume that its implementation is consistent with the definition of equality. Since your implementation is not, you are violating that assumption and therefore getting inconsistent results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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