Какая правильная реализация для GetHashCode () для classов сущностей?

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

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

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 ()?

Если вы извлекаете из чего-то, что уже переопределяет 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 ().

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

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

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

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

То, что ответил Jon Skeet, является хорошим решением, однако вы можете захотеть добавить неконтролируемый блок кода, чтобы обеспечить полное переполнение

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

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

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

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

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

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