2009-02-03 4 views
15

Ниже приведен пример реализации переопределения Object.Equals() для базового класса сущности, из которого выводятся все остальные объекты в приложении.Какова правильная реализация для GetHashCode() для классов сущностей?

Все классы сущностей имеют свойство Id, которое является нулевым int. (Это первичный ключ любой таблицы класс сущности соответствует.)

public override bool Equals(object obj) 
     { 
      if (obj == null || GetType() != obj.GetType()) 
       return false; 

      if (base.Equals(obj)) 
       return true; 

      return Id.HasValue && ((EntityBase) obj).Id.HasValue && 
        Id.Value == ((EntityBase) obj).Id.Value; 
     } 

Учитывая эту реализацию Equals(), как вы правильно реализовать GetHashCode()?

+0

Для записи, имеющий значение NULL для идентификатора, является ужасной идеей. Идентификаторы должны быть всегда всегда GUID и определенно не обнуляемы. –

ответ

23

Если вы вывод из чего-то, что уже переопределяет GetHashCode я бы реализовать его как:

public override int GetHashCode() 
{ 
    unchecked 
    { 
     int hash = 37; 
     hash = hash * 23 + base.GetHashCode(); 
     hash = hash * 23 + Id.GetHashCode(); 
     return hash; 
    } 
} 

Нулевое значение Id возвращает 0 для Id.GetHashCode().

Если ваш класс просто происходит от объекта, я бы просто вернуть Id.GetHashCode() - вы не хотите включить object.GetHashCode реализацию в вашей хэш-код, так как это в основном заканчивается время идентификатор объекта.

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

+9

Для тех, кто задавался вопросом, как и я: 23 и 37 являются произвольными числами, которые являются совместными. Джон заявил об этом в аналогичном ответе здесь: http://www.eggheadcafe.com/software/aspnet/29483139/override-gethashcode.aspx –

+0

@JonSkeet не согласен ли вы, что добавление 'base.GetHashCode()' будет использовать Object .GetHashCode, который использует адрес памяти. В этом случае GetHashCode не имеет свойства, которое, когда Equals возвращает true для двух объектов, их хэш-коды должны быть одинаковыми? – Jaap

+0

@JonSkeet Я бы также добавил 'unchecked {}' к реализациям hashcode, чтобы избежать параметров компилятора ... Это краевой случай, который вы, вероятно, никогда не столкнетесь ... В любом случае ключевое слово unchecked является способом документирования этого в функции отлично. – Jaap

1

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

Предполагая, что это так, вы можете просто использовать значение Id как хеш для всех допустимых значений, а затем использовать фиксированный хэш для нуля. Я не могу вспомнить, что наиболее подходит для этого, но я бы предположил случайное выбранное значение для null (произвольно выбранное до компиляции, а не во время выполнения), или медианное значение допустимых значений Id (т.е. на полпути между 0 и int. Максимум).

+1

Он не должен быть неизменным на всю жизнь - только с момента, когда он впервые вставлен в карту или что-то еще.Все в порядке, чтобы создать экземпляр, обходиться с ним, поместить его в карту и * затем * прекратить возиться с ним. –

+0

Это справедливая точка. Я сделаю редактирование. –

2

Что ответил Джон Скит является хорошим решением, однако, вы можете захотеть добавить непроверенной блок кода, чтобы целый перелив

unchecked 
{ 
    int hash = ...; 
    return hash 
} 

https://msdn.microsoft.com/en-us/library/khy08726(v=vs.140).aspx

Если ни проверено, ни бесконтрольно указан, контекст по умолчанию зависит от внешних факторов, таких как параметры компилятора.

Я хотел бы также добавить, опять же, что использование base.GetHashCode() на ПОКО-х будет вызывать по умолчанию object.GetHashCode. Это определенно не то, что вы хотите ...

+0

'unchecked' является значением по умолчанию и не является необходимым. –

+0

«Если ни отмеченные, ни снятые флажки не указаны, контекст по умолчанию зависит от внешних факторов, таких как параметры компилятора». https://msdn.microsoft.com/en-us/library/khy08726(v=vs.140).aspx Итак, если вы напишете такой код, я бы положил его туда. Это также форма документации. – Jaap

+1

Добавили непроверенную часть к моему собственному ответу сейчас, кстати. –

2

Как насчет того, чтобы использовать этот тип как часть хеш-кода?
Будет ли это хорошей реализацией?

public class Foo 
{ 
    public int Id { get; set; } 

    // other properties here 
    // ...... 

    public override int GetHashCode() 
    { 
     int hash = 37; 
     hash = hash * 23 + typeof(Foo).GetHashCode(); 
     hash = hash * 23 + Id.GetHashCode(); 
     return hash; 
    } 
} 
Смежные вопросы