Какая правильная реализация для GetHashCode() для классов сущностей?
Ниже приведен пример реализации переопределения Object.Equals() для базового класса сущности, из которого выводятся все другие сущности в приложении.
Все классы сущностей имеют свойство Id, значение которого равно null. (Это первичный ключ любой таблицы, которой соответствует класс объекта.)
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()?
Ответы
Ответ 1
Если вы получаете что-то, что уже переопределяет 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().
Если ваш класс просто получен из Object, я бы просто вернул Id.GetHashCode()
- вы не хотите включать реализацию object.GetHashCode
в свой хеш-код, поскольку это в основном заканчивается идентификацией объекта.
Обратите внимание, что ваше определение равенства не вернет true
, если ни один объект не имеет идентификатор, но тот же хэш-код будет возвращен из обоих объектов. Возможно, вы захотите рассмотреть возможность изменения реализации Equals.
Ответ 2
Что ответил Jon Skeet - это хорошее решение, однако вы можете захотеть добавить неконтролируемый блок кода, чтобы можно было переполнять целое число
unchecked
{
int hash = ...;
return hash
}
https://msdn.microsoft.com/en-us/library/khy08726(v=vs.140).aspx
Если ни один из них не установлен или не отмечен, контекст по умолчанию зависит от внешних факторов, таких как параметры компилятора.
Я также хотел бы добавить, что использование base.GetHashCode()
в POCO вызовет по умолчанию object.GetHashCode
. Это определенно не то, что вы хотите...
Ответ 3
Как использовать тип как часть хеш-кода?
Будет ли это хорошей реализацией?
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;
}
}
Ответ 4
Вы можете корректно реализовать GetHashCode()
, если свойство Id
является неизменным для времени жизни экземпляра (или, по крайней мере, для того времени, когда его хэш должен использоваться, например, когда объект находится на карте или другая коллекция, требующая хэш).
Предполагая, что это так, вы можете просто использовать значение Id
как хеш для всех допустимых значений, а затем использовать фиксированный хэш для нуля. Я не могу вспомнить, что наиболее подходит для этого, но я бы предположил случайное выбранное значение для null (произвольно выбранное до компиляции, а не во время выполнения) или медианное значение допустимых значений Id
(т.е. на полпути между 0 и int.Max).