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





По умолчанию он делает это на основе ссылки на объект, но это означает, что это один и тот же объект, поэтому оба возвращают один и тот же хеш. Но хеш должен быть основан на значении, как в случае строкового класса. «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();
}
@thewhiteambit Нет. Я предполагаю, что не все объекты являются хорошими кандидатами на ключи хеш-таблицы. То, что они может хешируются, не означает, что они должен. И тот факт, что GetHashCode является методом базового класса Object, является просто плохим дизайнерским решением для языка C#. Более того, я не говорю, что вы должны сделать каждый тип ключа хеш-таблицы неизменяемым - просто это очень помогает.
Вы правы, я просто указывал на предложение «Чтобы понять это, часто проще всего сделать хешируемые объекты неизменяемыми» - и поскольку все объекты являются хешируемыми (из-за неправильного выбора дизайна), эта самая попытка сделает все хешируемые объекты (равно всем объектам ) неизменный. Но я полагаю, вы имели в виду не это. Вы, вероятно, хотели просто сделать все объекты, которые нужно хранить в Hash-Collections, неизменяемыми.
@thewhiteambit Ах да. Например: если у вас есть контроль над типом объекта, который вы собираетесь хешировать, (настоятельно) подумайте о том, чтобы сделать его неизменяемым.
Не уверен, о какой документации 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 возвращают один и тот же хэш-код, если они представляют одно и то же строковое значение, и используют все символы в строке для генерации этого хэш-значения.
Это был самый подробный и запутанный ответ, который я когда-либо читал. Это оставило меня в еще большем замешательстве, чем то, с чего я начал.
Правила 1 и 3 на самом деле не противоречат друг другу.
Для ссылочного типа хэш-код является производным от ссылки на объект - измените свойство объекта, и ссылка останется той же.
Для типов значений хэш-код является производным от значения, измените свойство типа значения, и вы получите полностью новый экземпляр типа значения.
Я не могу точно знать, как Object.GetHashCode реализован в настоящий .NET Framework, но в Rotor он использует индекс SyncBlock для объекта как хэш-код. В сети есть несколько сообщений об этом в блогах, но большинство из них относятся к 2005 году.
Очень хорошее объяснение того, как работать с GetHashCode (помимо правил Microsoft), дано в блоге Эрика Липпертса (соавтор разработчика C#) в статье «Рекомендации и правила для GetHashCode». Не рекомендуется добавлять здесь гиперссылки (поскольку они могут стать недействительными), но это того стоит, и при условии, что указанная выше информация, вероятно, все равно найдет ее в случае потери гиперссылки.
Поскольку в C# все объекты хешируются (GetHashCode () является частью очень простого типа Object), вы можете предложить сделать все объекты неизменяемыми - не очень практично, не так ли?