Правильная реализация GetHashCode

Я хотел бы услышать от сообщества, как мне следует реализовать GetHashCode (или переопределить его) для моего объекта. Я понимаю, что мне нужно это сделать, если я переопределю метод equals. Я реализовывал это довольно много раз, иногда просто вызывая базовый метод. Я понимаю, что мой объект должен соответствовать другому экземпляру объекта, если он содержит такие же детали (члены). Как лучше всего получить хэш-код от членов класса?


person Zivka    schedule 25.01.2012    source источник
comment
Считайте закрытие дубликатом stackoverflow.com/a/720282/93922   -  person Tetsujin no Oni    schedule 26.01.2012
comment
Прежде чем вы это сделаете, я рекомендую вам прочитать и понять мою статью об этом: blogs.msdn.com/b/ericlippert/archive/2011/02/28/   -  person Eric Lippert    schedule 26.01.2012
comment
Спасибо, я понимаю, что это может быть частый вопрос, и мне следовало бы побольше узнать. Спасибо за вашу помощь   -  person Zivka    schedule 26.01.2012
comment
Если вы не собираетесь использовать класс в качестве ключа для хеш-таблицы, вам на самом деле не нужно переопределять GetHashCode.   -  person phoog    schedule 26.01.2012
comment
@EricLippert Ваша связанная статья кажется недоступной к сегодняшнему дню (2021 г.). У вас есть актуальная версия?   -  person Fildor    schedule 28.01.2021
comment
@Fildor: Это здесь: ericlippert.com/2011/ 28 февраля / guide-and-rules-for-gethashcode   -  person Eric Lippert    schedule 28.01.2021
comment
@EricLippert Отлично, спасибо!   -  person Fildor    schedule 29.01.2021


Ответы (1)


Допустим, ваш класс выглядит так:

class Frob {
    public string Foo { get; set; }
    public int Bar { get; set; }
    public double FooBar { get; set; }
}

Допустим, вы определяете equals так, что два экземпляра Frob равны, если их Foo и их Bar равны, но FooBar не имеет значения.

Затем вы должны определить GetHashCode в терминах Foo и Bar. Один из способов такой:

return this.Foo.GetHashCode() * 17 + this.Bar.GetHashCode();

По сути, вы просто хотите включить все поля, которые используются для определения равенства. Один из способов - просто продолжать накапливать и умножать на 17, как это сделал я. Это быстро, просто, правильно и обычно дает хорошее распространение.

person jason    schedule 25.01.2012
comment
Я чувствую, что это должен быть сам вопрос, но почему 23? - person George Duckett; 26.01.2012
comment
Главный коэффициент, позволяющий различать {2,1} и {1,2}. - person Tetsujin no Oni; 26.01.2012
comment
возможно, укажите, что GetHashCode только сообщает вам, могут ли считаться равными два объекта. По-прежнему существует вероятность хеш-коллизии. - person geofftnz; 26.01.2012
comment
@geofftnz Или, более конкретно, GetHashCode используется для определения того, в какую корзину поместить объект при сохранении его в хэше. Если ваш GetHashCode плохо различается, он создаст длинный список, в котором будет выполняться линейный поиск при возврате элементов из HashTable, и он будет очень плохим ключом (читай: вызывающим коллизию) для Словаря. - person Tetsujin no Oni; 26.01.2012
comment
stackoverflow.com/a/720282/93922 немного расширяет это. - person Tetsujin no Oni; 26.01.2012
comment
@TetsujinnoOni идет еще дальше: в некоторых случаях должно быть сложно отменить хэш, чтобы злоумышленникам было сложнее превратить вашу хеш-таблицу в связанный список (по сложности) и убить ваш процессор. Поэтому в начале января 2012 года мы бегали по серверам с исправлениями. - person geofftnz; 26.01.2012
comment
Будьте осторожны с реализацией GetHashCode()Equals()) в зависимости от изменяемых данных, как здесь. Если вы поместите такой объект в словарь на основе хешей, а затем измените его, словарь больше не будет работать правильно. В идеале GetHashCode()Equals()) должны зависеть только от неизменяемых данных. - person svick; 26.01.2012
comment
Одно из ожиданий от GetHashCode() реализаций состоит в том, что они должны возвращать одно и то же значение в течение всего времени существования объекта. Реализация, основанная на изменяемом состоянии, недействительна и вызовет действительно ужасные для диагностики ошибки. - person Paul Turner; 28.01.2014
comment
@Tragedian: Основное требование - GetHashCode должен отражать поведение Equals. Если два объекта могут быть равны в один момент, но позже могут быть неравными, или наоборот, то необходимо убедиться, что никакие ссылки на такие объекты не подвергаются воздействию чего-либо, что может неожиданно изменить их состояние. К сожалению, в системе типов нет ничего, что указывало бы на это. - person supercat; 29.01.2014
comment
@Tragedian: .NET не имеет инструментов для практического контроля мутаций. Поэтому требовать неизменяемости бесполезно и непрактично - как правило, вам потребуются данные, которые не изменяются, а не данные, которые нельзя изменить. Конечно, там, где readonly участники практичны - во что бы то ни стало, используйте их - просто не ожидайте, что это будет практично везде. - person Eamon Nerbonne; 05.06.2014
comment
Я думаю, что расчет должен быть заключен в ключевое слово unchecked, например: unchecked(this.Foo.GetHashCode() * 17 + this.Bar.GetHashCode()). Сумма может быть больше int.MaxValue. - person mortb; 14.02.2018
comment
@mortb - Насколько я понимаю, параметр unchecked применяется только тогда, когда переполнение / потеря значимости может быть обнаружено во время компиляции (т.е. выражение содержит только константы). Выражения, содержащие непостоянные термины, по умолчанию не отмечены. - person Quintus; 22.04.2019
comment
@Quintus: Я поправляюсь. При назначении Bar = int.MaxValue; Foo = "something", выполнении функции GetHashCode() и отсутствии ключевого слова unchecked код будет выполняться и генерировать отрицательный хэш-код без сбоя, поэтому ключевое слово unchecked не требуется. - person mortb; 23.04.2019
comment
C # по умолчанию не отмечен, а VB.Net по умолчанию - отмечен (src) - но оба могут быть переопределены с помощью флага компилятора, поэтому, чтобы быть на 100% уверенным в своих намерениях, может быть хорошей практикой в ​​любом случае использовать ключевое слово unchecked - person Zac Faragher; 08.10.2019
comment
У меня вопрос по поводу этого 17: почему 17? - person Eru; 12.03.2021