Подтвердить что ты не робот

Предупреждение: "... переопределяет Object.Equals(объект o), но не переопределяет Object.GetHashCode()"

Я переопределял Equals() моего класса для сравнения значений идентификатора типа Guid.

Затем Visual Studio предупредила:

... переопределяет Object.Equals(объект o), но не переопределяет Object.GetHashCode()

Итак, я также переопределяю его GetHashCode() следующим образом:

public partial class SomeClass
{
    public override bool Equals(Object obj)
    {
        //Check for null and compare run-time types.
        if (obj == null || this.GetType() != obj.GetType()) return false;

        return this.Id == ((SomeClass)obj).Id;
    }

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }
}

Кажется, что сработало. Правильно ли я сделал это? Запомнить идентификатор типа Guid. Имеет ли значение, что мой класс является объектом Entity Framework?

4b9b3361

Ответ 1

Как говорили другие, использование Reflection in Equals кажется изворотливым. Оставив это в стороне, позвольте сосредоточиться на GetHashCode.

Основное правило для GetHashCode, которое вы не должны нарушать, , если два объекта равны, то они должны иметь одинаковый хэш-код. Или эквивалентный способ сказать, что , если два объекта имеют разные хэш-коды, то они должны быть неравными. Ваша реализация выглядит хорошо там.

Вы можете нарушать обратное. То есть, если два объекта имеют один и тот же хэш-код, тогда им разрешено быть равными или неравными, как вы сочтете нужным.

Я предполагаю, что "Id" является непреложным свойством. Если "Id" может меняться в течение всего жизненного цикла объекта, тогда у вас могут возникнуть проблемы при помещении объекта в хеш-таблицу. Учтите, что при вычислении равенства и хеш-кода используются только неизменные свойства.

Ваша реализация выглядит хорошо, но тот факт, что вы задаете вопрос, указывает на то, что у вас может не быть полного понимания всех тонких факторов, которые идут на создание реализации GetHashCode. Хорошее место для начала - моя статья на эту тему:

http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

Ответ 2

Это выглядит правильно для меня. Всякий раз, когда я делаю что-то подобное, я обычно также реализую IEquatable, чтобы сравнения между переменными одного и того же типа времени компиляции были немного более эффективными.

public partial class SomeClass : IEquatable<SomeClass>
{
     public override bool Equals(Object obj)
     {
         return Equals(obj as SomeClass);
     }
     public bool Equals(SomeClass obj)
     {
         if (obj == null) 
             return false;
         return Id == obj.Id;
     }
     public override int GetHashCode()
     {
         return Id.GetHashCode();
     }
} 

Эта структура также позволяет более производному объекту с тем же идентификатором сравниваться как равный менее производному объекту. Если это не желаемое поведение, вам также придется сравнивать типы, как в вопросе.

if (obj.GetType() != typeof(SomeClass)) return false;

Ответ 3

Так как вы не имеете дело с закрытым классом, я бы рекомендовал не проверять равенство классов, как этот this.GetType() != obj.GetType(). Любой подкласс класса SomeClass должен также участвовать в Equals, поэтому вы можете использовать его вместо этого:

if (obj as SomeClass == null) return false;

Ответ 4

Традиционно Equals реализуется таким образом, что два объекта будут только "равно", если они абсолютно одинаковы во всех отношениях. Например, если у вас есть два объекта, которые представляют один и тот же объект в базе данных, но там, где у другого есть другое свойство Name, чем другие, объекты не считаются "равными" и должны избегать создания одного и того же "Hashcode", если возможно.

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

Если у вас есть структура данных (например, HashSet), где вы специально хотите определить равенство на основе значения идентификатора, вы можете предоставить конкретную реализацию IEqualityComparer для этой структуры данных.

Ответ 5

Вы получили отличные ответы на свой первый вопрос:

Я сделал это правильно?

Я отвечу на ваш второй вопрос

Имеет ли значение, что мой класс является объектом Entity Framework?

Да, это очень важно. Структура Entity использует HashSet много внутри. Например, динамические прокси используют HashSet для представления свойств навигации для коллекции, а EntityObject используют EntityCollection, который, в свою очередь, использует HashSet внутренне.