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

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

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

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

5 ответов


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

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

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

Доменный Дизайн делает различие между объекты и Предметов Стоимостью. Это хорошее различие для наблюдения, так как оно определяет, как вы реализуете равные.

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

Предметов Стоимостью равны, если все их (важные) составные элементы равны друг другу.

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


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

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

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

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


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


я наткнулся на этот старый вопрос и, ИМХО, я не нашел никакого ответа, ясно и просто сформулировал первоначальный вопрос, сформулированный @tucaz.

Я могу согласиться со многими соображениями, разделяемыми выше (или ниже :D), но "точка вопроса" была пропущена (я думаю).

при условии, что:

  • равенство требуется для сущностей
  • Entity-объекты можно считать равными, если они сопоставляют одну и ту же сущность, id est они ссылаются на одну и ту же "сущность Ключ"
  • пример, показанный @tucaz, просто упоминает " Id " (см. реализованный GetHashCode ())... не говоря уже о багги равна(...)

Я могу предположить, что одна простая реализация может быть:

public class MyEntity: IEquatable<MyEntity> {
    int Id;

    public MyEntity(int id){
        Id = id;
    }

    public override bool Equals(object obj) => Equals(obj as MyEntity);
    public bool Equals(MyEntity obj) => obj != null && Id == obj.Id;
    public override int GetHashCode() => Id;
}

вот и все!