Состояние в IEquatable <T>.Equals - PullRequest
2 голосов
/ 09 июня 2011

Я реализовал IEquatable<T> для сравнения объектов в двух списках, однако я хочу сделать это условно так:

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

Что-то здесь не так, и оно никогда не переходит в другое состояние. Может кто-нибудь сказать мне, где я иду не так?

- Использование -

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 клиента:

Cust1: Имя - «А» Фамилия - "Z" MiddleName - "Y" ZipCode - "11111"

Cust2: FirstName - "B" Фамилия - «Х» MiddleName - "W" ZipCode - "44444"

ListB имеет 2 клиентов:

Cust1: Имя - «А» Фамилия - "Z" MiddleName - "Y" ZipCode - "11111"

Cust2: FirstName - "B" Фамилия - "G" MiddleName - "G" ZipCode - "44444"

Здесь, когда я говорю ListA.Except (ListB), он должен сравнивать CustA с FirstName, MiddleName, LastName, поскольку он принадлежит Zipcode 11111 и CustB только с FirstName, и то же самое применяется, когда я говорю ListB.Except (ListA). В текущей реализации Equals происходит то, что она отлично работает с ListA.Except (ListB), но когда я говорю ListB.Except (ListA), она сравнивает FirstName, LastName и Middlename для CustB.

Ответы [ 2 ]

5 голосов
/ 09 июня 2011

Я почти уверен, что вы не правильно реализовали GetHashCode().Всякий раз, когда вы переопределяете Equals, вы должны переопределять GetHashCode(), чтобы они были согласованными.

Условие состоит в том, что если для двух объектов o1.Equals(o2) возвращает true, то результаты GetHashCode должны быть одинаковыми.

Поскольку Except использует хэш-набор внутри, реализация GetHashCode() уместна здесь.Без хэширования его сложность возрастет с O (n) до O (n ^ 2), что явно нежелательно.

В дополнение к этому Equals должно быть симметричным, а ваше - нет.


Глядя на вашу GetHashCode() функцию, это явно неправильно.Он учитывает поля, которые Equals не делает.

Всякий раз, когда ваш код входит в then часть if, он может принимать во внимание FirstName, LastName и MiddleName,Когда ваш код входит в часть 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

2 голосов
/ 10 июня 2011

Что касается симметрии ваших функций equals и hashcode и исходя из того, что вы сказали в комментариях к другому ответу, я считаю, что это реализация, которая вам нужна:

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 имеют одинаковые имена), задайте себе следующие вопросы:

  1. Является ли клиент А равным клиенту Б? По вашему коду ответ - нет.
  2. Клиент B равен клиенту A? По вашему коду ответ - да.

При сравнении клиента A с клиентом B ваша реализация будет использовать имя, фамилию и отчество. Но при сравнении клиента B с клиентом A ваша реализация будет использовать только имя. Это нарушает фундаментальное определение равенства :

Симметричное свойство состояний:

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

Любые встроенные функции, которые полагаются на ваш метод Equals, предполагают, что его реализация соответствует определению равенства. Поскольку ваша реализация не соответствует действительности, вы нарушаете это предположение и, следовательно, получаете противоречивые результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...