Является ли Object.GetHashCode () уникальным для ссылки или значения?

Документация MSDN на Object.GetHashCode () описывает 3 противоречащих друг другу правила того, как этот метод должен работать.

  1. Если два объекта одного типа представляют одно и то же значение, хеш-функция должна возвращать одно и то же постоянное значение для любого объекта.
  2. Для лучшей производительности хеш-функция должна генерировать случайное распределение для всех входных данных.
  3. Хеш-функция должна возвращать точно такое же значение независимо от любых изменений, внесенных в объект.

Правила 1 и 3 мне противоречат.

Возвращает ли Object.GetHashCode () уникальный номер на основе ценить объекта или ссылка объекта. Если я переопределю метод, я могу выбрать, что использовать, но я хотел бы знать, что используется внутри, если кто-нибудь знает.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
25
0
8 097
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

По умолчанию он делает это на основе ссылки на объект, но это означает, что это один и тот же объект, поэтому оба возвращают один и тот же хеш. Но хеш должен быть основан на значении, как в случае строкового класса. «a» и «b» будут иметь другой хэш, но «a» и «a» вернут один и тот же хеш.

Ответ принят как подходящий

Rules 1 & 3 are contradictory to me.

В некоторой степени да. Причина проста: если объект хранится в хеш-таблице и, изменяя его значение, вы меняете его хэш, тогда хеш-таблица потеряла значение, и вы не можете найти его снова, запросив хеш-таблицу. Важно, чтобы при хранении объектов в хэш-таблице они сохраняли свое хеш-значение.

Чтобы понять это, часто проще всего сделать хешируемые объекты неизменяемыми, таким образом избегая всей проблемы. Однако достаточно сделать неизменными только те поля, которые определяют хеш-значение.

Рассмотрим следующий пример:

struct Person {
    public readonly string FirstName;
    public readonly string Name;
    public readonly DateTime Birthday;

    public int ShoeSize;
}

Люди редко меняют день рождения, и большинство людей никогда не меняют своего имени (кроме случаев женитьбы). Однако их размер обуви может произвольно увеличиваться или даже уменьшаться. Поэтому разумно идентифицировать людей по имени и дате рождения, но не по размеру обуви. Хеш-значение должно отражать это:

public int GetHashCode() {
    return FirstName.GetHashCode() ^ Name.GetHashCode() ^ Birthday.GetHashCode();
}

Поскольку в C# все объекты хешируются (GetHashCode () является частью очень простого типа Object), вы можете предложить сделать все объекты неизменяемыми - не очень практично, не так ли?

thewhiteambit 08.01.2016 16:10

@thewhiteambit Нет. Я предполагаю, что не все объекты являются хорошими кандидатами на ключи хеш-таблицы. То, что они может хешируются, не означает, что они должен. И тот факт, что GetHashCode является методом базового класса Object, является просто плохим дизайнерским решением для языка C#. Более того, я не говорю, что вы должны сделать каждый тип ключа хеш-таблицы неизменяемым - просто это очень помогает.

Konrad Rudolph 09.01.2016 04:42

Вы правы, я просто указывал на предложение «Чтобы понять это, часто проще всего сделать хешируемые объекты неизменяемыми» - и поскольку все объекты являются хешируемыми (из-за неправильного выбора дизайна), эта самая попытка сделает все хешируемые объекты (равно всем объектам ) неизменный. Но я полагаю, вы имели в виду не это. Вы, вероятно, хотели просто сделать все объекты, которые нужно хранить в Hash-Collections, неизменяемыми.

thewhiteambit 12.01.2016 16:52

@thewhiteambit Ах да. Например: если у вас есть контроль над типом объекта, который вы собираетесь хешировать, (настоятельно) подумайте о том, чтобы сделать его неизменяемым.

Konrad Rudolph 12.01.2016 16:54

Не уверен, о какой документации MSDN вы имеете в виду. Глядя на текущую документацию по Object.GetHashCode (http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx), можно найти следующие «правила»:

  • Если два объекта сравниваются как равные, метод GetHashCode для каждого объекта должен возвращать одно и то же значение. Однако, если два объекта не сравниваются как равные, методы GetHashCode для двух объектов не должны возвращать разные значения.

  • Метод GetHashCode для объекта должен последовательно возвращать один и тот же хэш-код до тех пор, пока не будет изменено состояние объекта, определяющее возвращаемое значение метода Equals объекта. Обратите внимание, что это верно только для текущего выполнения приложения и что другой хэш-код может быть возвращен, если приложение запускается снова.

  • Для лучшей производительности хеш-функция должна генерировать случайное распределение для всех входных данных.

Если вы имеете в виду второй пункт маркера, ключевые фразы здесь: «до тех пор, пока нет изменений в состоянии объекта» и «истинно только для текущего выполнения приложения».

Также из документации,

A hash function is used to quickly generate a number (hash code) that corresponds to the value of an object. Hash functions are usually specific to each Type and must use at least one of the instance fields as input. [Emphasis added is mine.]

Что касается фактической реализации, в ней четко указано, что производные классы могут подчиняться реализации Object.GetHashCode если и только если, этот производный класс определяет равенство значений как ссылочное равенство, а тип не является типом значения. Другими словами, реализация Object.GetHashCode по умолчанию будет основана на ссылочном равенстве, поскольку нет реальных полей экземпляра для использования и, следовательно, не гарантирует уникальных возвращаемых значений для различных объектов. В противном случае ваша реализация должна быть специфичной для вашего типа и должна использовать хотя бы одно из полей вашего экземпляра. Например, реализация String.GetHashCode возвращает идентичные хэш-коды для идентичных строковых значений, поэтому два объекта String возвращают один и тот же хэш-код, если они представляют одно и то же строковое значение, и используют все символы в строке для генерации этого хэш-значения.

Это был самый подробный и запутанный ответ, который я когда-либо читал. Это оставило меня в еще большем замешательстве, чем то, с чего я начал.

bleepzter 13.01.2015 04:49

Правила 1 и 3 на самом деле не противоречат друг другу.

Для ссылочного типа хэш-код является производным от ссылки на объект - измените свойство объекта, и ссылка останется той же.

Для типов значений хэш-код является производным от значения, измените свойство типа значения, и вы получите полностью новый экземпляр типа значения.

Я не могу точно знать, как Object.GetHashCode реализован в настоящий .NET Framework, но в Rotor он использует индекс SyncBlock для объекта как хэш-код. В сети есть несколько сообщений об этом в блогах, но большинство из них относятся к 2005 году.

Очень хорошее объяснение того, как работать с GetHashCode (помимо правил Microsoft), дано в блоге Эрика Липпертса (соавтор разработчика C#) в статье «Рекомендации и правила для GetHashCode». Не рекомендуется добавлять здесь гиперссылки (поскольку они могут стать недействительными), но это того стоит, и при условии, что указанная выше информация, вероятно, все равно найдет ее в случае потери гиперссылки.

Другие вопросы по теме