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

Какая лучшая стратегия для Equals и GetHashCode?

Я работаю с моделью домена и думал о различных способах реализации этих двух методов в .NET. Какова ваша предпочтительная стратегия?

Это моя текущая реализация:

    public override bool Equals(object obj)
    {
        var newObj = obj as MyClass;

        if (null != newObj)
        {
            return this.GetHashCode() == newObj.GetHashCode();
        }
        else
        {
            return base.Equals(obj);
        }
    }

    //Since this is an entity I can use it´s Id
    //When I don´t have an Id I usually make a composite key of the properties
    public override int GetHashCode()
    {
        return String.Format("MyClass{0}", this.Id.ToString()).GetHashCode();
    }
4b9b3361

Ответ 1

Предполагая, что экземпляры равны, потому что хэш-коды равны, неверны.

Я предполагаю, что ваша реализация GetHashCode в порядке, но я обычно использую похожие вещи:

public override int GetHashCode() {
    return object1.GetHashCode ^ intValue1 ^ (intValue2 << 16);
}

Ответ 2

Domain-Driven Design делает различие между Entities и объектами Value. Это хорошее различие, чтобы наблюдать, поскольку он определяет, как вы реализуете Equals.

Объекты равны, если их идентификаторы равны друг другу.

Объекты Value равны, если все их (важные) составные элементы равны друг другу.

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

Ответ 3

Ни один из ответов здесь действительно не попал мне в голову. Поскольку вы уже сказали, что не можете использовать Id для равенства, и вам нужно использовать набор свойств, вот лучший способ сделать это. Примечание. Я считаю, что это не лучший способ реализовать Equals и GetHashCode. Это лучшая версия кода OP.

public override bool Equals(object obj)
{
   var myClass = obj as MyClass;

   if (null != myClass)
   {
      // Order these by the most different first.
      // That is, whatever value is most selective, and the fewest
      // instances have the same value, put that first.
      return this.Id == myClass.Id
         && this.Name == myClass.Name
         && this.Quantity == myClass.Quantity
         && this.Color == myClass.Color;
   }
   else
   {
      // Not sure this makes sense!
      return base.Equals(obj);
   }
}

public override int GetHashCode()
{
   int hash = 19;
   unchecked { // allow "wrap around" in the int
      hash = hash * 31 + this.Id; // assuming integer
      hash = hash * 31 + this.Name.GetHashCode();
      hash = hash * 31 + this.Quantity; // again assuming integer
      hash = hash * 31 + this.Color.GetHashCode();
   }
   return hash;
}

См. этот ответ Джона Скита для некоторых причин этого. Использование xor не является хорошим, потому что различные наборы данных могут привести к тому же хэшу. Этот метод обертывания с помощью простых чисел (начальные значения 19 и 31 выше или другие значения, которые вы выбираете) лучше выполняет сегментирование в "ведра", у которых мало коллизий.

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

Кроме того, я не уверен, что ваша реализация Equals имеет смысл. Когда два объекта сравниваются для равенства, сначала сравниваются их значения GetHashCode. Только если они отличаются друг от друга, выполняется метод Equals (так что, если два объекта, хэш которых имеет одинаковое значение, различаются, это будет обнаружено). Поскольку реализация GetHashCode не относится к base, для вашего метода Equals нет смысла делать это.

Ответ 4

Хаскоды могут столкнуться, поэтому я не думаю, что они являются хорошим способом сравнения равенства. Вы должны сравнить базовые значения, которые делают объекты "равными". См. @Jon Skeet ответ на этот вопрос: Каков наилучший алгоритм для переопределенного System.Object.GetHashCode? для лучшей реализации GetHashCode, если ваше равенство включает в себя несколько свойств. Если это всего лишь одно свойство, вы можете просто использовать его hashcode.