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