Как работает реализация по умолчанию GetHashCode()
? И достаточно ли эффективно и эффективно он обрабатывает структуры, классы, массивы и т. Д.?
Я пытаюсь решить, в каких случаях я должен упаковать свою собственную, и в каких случаях я могу смело полагаться на реализацию по умолчанию, чтобы преуспеть. Я не хочу изобретать велосипед, если это вообще возможно.
.net
hash
gethashcode
Fung
источник
источник
GetHashCode()
он был переопределен) с помощьюSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj)
Ответы:
InternalGetHashCode сопоставляется с функцией ObjectNative :: GetHashCode в CLR, которая выглядит следующим образом:
Полная реализация GetHashCodeEx довольно велика, поэтому проще просто ссылаться на исходный код C ++ .
источник
string
переопределяетGetHashCode
. С другой стороны, предположим, что вы хотите вести подсчет того, сколько раз различные элементы управления обрабатываютPaint
события. Вы можете использоватьDictionary<Object, int[]>
(каждыйint[]
хранится будет содержать только один элемент).Для класса значения по умолчанию, по сути, являются ссылочным равенством, и это обычно хорошо. Если вы пишете структуру, то чаще встречается переопределение равенства (не в последнюю очередь, чтобы избежать бокса), но очень редко вы пишете структуру в любом случае!
При переопределении равенства у вас всегда должно быть совпадение
Equals()
иGetHashCode()
(т. Е. Для двух значений, если оноEquals()
возвращает true, они должны возвращать один и тот же хеш-код, но обратное не требуется), и обычно также предоставляются операторы==
/!=
и часто реализоватьIEquatable<T>
тоже.Для генерации хеш-кода обычно используется факторизованная сумма, поскольку это позволяет избежать коллизий парных значений - например, для базового хеша из 2 полей:
Это имеет то преимущество, что:
и т. д. - что может быть обычным делом, если использовать невзвешенную сумму или xor (
^
) и т. дисточник
unchecked
. К счастью,unchecked
это по умолчанию в C #, но было бы лучше сделать это явным; отредактированоВ документации по
GetHashCode
методу для объекта говорится, что «реализация по умолчанию этого метода не должна использоваться в качестве уникального идентификатора объекта для целей хеширования». а для ValueType говорится: «Если вы вызываете метод GetHashCode производного типа, возвращаемое значение вряд ли подойдет для использования в качестве ключа в хэш-таблице». ,Основные типы данных , такие как
byte
,short
,int
,long
,char
иstring
реализовать метод хорошо GetHashCode. Некоторые другие классы и структуры, такие как,Point
например, реализуютGetHashCode
метод, который может подходить или не подходить для ваших конкретных потребностей. Вы просто должны попробовать это, чтобы увидеть, достаточно ли это хорошо.Документация для каждого класса или структуры может сказать вам, переопределяет ли она реализацию по умолчанию или нет. Если он не переопределяет, вы должны использовать свою собственную реализацию. Для любых классов или структур, которые вы создаете сами, где вам нужно использовать
GetHashCode
метод, вы должны создать свою собственную реализацию, которая использует соответствующие члены для вычисления хеш-кода.источник
Поскольку я не смог найти ответ, который объясняет, почему мы должны переопределять
GetHashCode
иEquals
для пользовательских структур и почему реализация по умолчанию "вряд ли подойдет для использования в качестве ключа в хэш-таблице", я оставлю ссылку на этот блог Почта , которое объясняет, почему с реальным примером проблемы, которая произошла.Я рекомендую прочитать весь пост, но вот резюме (выделение и пояснения добавлены).
Причина, по которой хэш по умолчанию для структур является медленным и не очень хорошим:
Реальная проблема, описанная в посте:
Итак, чтобы ответить на вопрос «в каких случаях я должен упаковать свою собственную, и в каких случаях я могу смело полагаться на реализацию по умолчанию», по крайней мере, в случае структур , вы должны переопределить
Equals
иGetHashCode
всякий раз, когда ваша пользовательская структура может использоваться как введите хэш-таблицу илиDictionary
.Я также рекомендовал бы реализовать
IEquatable<T>
в этом случае, чтобы избежать бокса.Как и в других ответах, если вы пишете класс , хэш по умолчанию, использующий равенство ссылок, обычно подходит, поэтому я не буду беспокоиться в этом случае, если вам не нужно переопределять
Equals
(тогда вам придется переопределятьGetHashCode
соответственно).источник
Вообще говоря, если вы переопределяете Equals, вы хотите переопределить GetHashCode. Причина этого в том, что оба используются для сравнения равенства вашего класса / структуры.
Равно используется при проверке Foo A, B;
если (A == B)
Поскольку мы знаем, что указатель вряд ли будет совпадать, мы можем сравнить внутренние элементы.
GetHashCode обычно используется хеш-таблицами. Хеш-код, сгенерированный вашим классом, всегда должен быть одинаковым для состояния, заданного классом.
Я обычно делаю,
Некоторые скажут, что хеш-код должен вычисляться только один раз за время существования объекта, но я не согласен с этим (и я, вероятно, ошибаюсь).
Используя реализацию по умолчанию, предоставляемую объектом, если у вас нет одинаковых ссылок на один из ваших классов, они не будут равны друг другу. Переопределив Equals и GetHashCode, вы можете сообщать о равенстве на основе внутренних значений, а не ссылки на объекты.
источник
Если вы просто имеете дело с POCO, вы можете использовать эту утилиту, чтобы немного упростить свою жизнь:
...
источник