Предупреждение: «... переопределяет Object.Equals (object o), но не переопределяет Object.GetHashCode ()» - PullRequest
23 голосов
/ 24 июня 2011

Я переопределил 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?

Ответы [ 5 ]

31 голосов
/ 24 июня 2011

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

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

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

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

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

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

8 голосов
/ 24 июня 2011

Это выглядит правильно для меня. Всякий раз, когда я делаю что-то подобное, я обычно также реализую 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();
     }
} 

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

if (obj.GetType() != typeof(SomeClass)) return false;
6 голосов
/ 24 июня 2011

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

if (obj as SomeClass == null) return false;
5 голосов
/ 25 июня 2011

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

Правильно ли я это сделал?

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

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

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

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

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

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

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

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