Почему важно переопределить GetHashCode, если переопределен метод Equals?

Учитывая следующий класс

public class Foo
{
    public int FooId { get; set; }
    public string FooName { get; set; }

    public override bool Equals(object obj)
    {
        Foo fooItem = obj as Foo;

        if (fooItem == null) 
        {
           return false;
        }

        return fooItem.FooId == this.FooId;
    }

    public override int GetHashCode()
    {
        // Which is preferred?

        return base.GetHashCode();

        //return this.FooId.GetHashCode();
    }
}

, я переопределил метод Equals, потому что Foo представляют строку для таблицы Foo. Какой метод переопределения GetHashCode является предпочтительным?

Почему важно переопределить GetHashCode?

1333
задан Rahmat Anjirabi 4 July 2019 в 15:37
поделиться

4 ответа

Да, важно, если Ваш объект будет использоваться в качестве ключа в словаре, или HashSet<T>, и т.д. - так как это используется (в отсутствие пользовательского IEqualityComparer<T>) к объектам группы в блоки. Если хэш-код для двух объектов не соответствует, они, май никогда не считают равным (Equals, никогда не будет просто называться).

GetHashCode() метод должен отразиться Equals логика; правила:

  • , если две вещи равны (Equals(...) == true) тогда, они должны возвращать то же значение для GetHashCode()
  • , если эти GetHashCode() равно, не необходимо для них быть тем же; это - коллизия, и Equals будет назван, чтобы видеть, является ли это подлинное равенство или нет.

В этом случае, это похоже" return FooId;", подходящее GetHashCode() реализация. При тестировании нескольких свойств распространено объединить их использующий код как ниже, уменьшить диагональные коллизии (т.е. так, чтобы new Foo(3,5) имел различный хэш-код к [1 113]):

unchecked // only needed if you're compiling with arithmetic checks enabled
{ // (the default compiler behaviour is *disabled*, so most folks won't need this)
    int hash = 13;
    hash = (hash * 7) + field1.GetHashCode();
    hash = (hash * 7) + field2.GetHashCode();
    ...
    return hash;
}

, О - для удобства, Вы могли бы также рассмотреть обеспечение == и != операторы при переопределении Equals и GetHashCode.

<час>

демонстрация А того, что происходит, когда Вы понимаете это превратно, здесь .

1233
ответ дан Marc Gravell 4 July 2019 в 15:37
поделиться

На самом деле очень трудно реализовать GetHashCode() правильно, потому что, в дополнение к правилам Marc, уже упомянутый, хэш-код, не должен изменяться в течение времени жизни объекта. Поэтому поля, которые используются для вычисления хэш-кода, должны быть неизменными.

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

128
ответ дан Jim McKeeth 4 July 2019 в 15:37
поделиться

Переопределением Равняется, Вы в основном заявляете, что Вы - тот, который знает лучше, как сравнить два экземпляра данного типа, таким образом, Вы, вероятно, будете лучшим кандидатом для обеспечения лучшего хэш-кода.

Это - пример того, как ReSharper пишет GetHashCode () функция для Вас:

public override int GetHashCode()
{
    unchecked
    {
        var result = 0;
        result = (result * 397) ^ m_someVar1;
        result = (result * 397) ^ m_someVar2;
        result = (result * 397) ^ m_someVar3;
        result = (result * 397) ^ m_someVar4;
        return result;
    }
}

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

52
ответ дан Nisarg 4 July 2019 в 15:37
поделиться

Именно, потому что платформа требует, чтобы два объекта были тем же, должен иметь тот же хэш-код. Если Вы переопределяете, равняется методу, чтобы сделать, специальное сравнение двух объектов и двух объектов считает тем же метод, то хэш-код двух объектов должен также быть тем же. (Словари и Хеш-таблицы полагаются на этот принцип).

10
ответ дан kemiller2002 4 July 2019 в 15:37
поделиться