Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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();
}

Ответ 2

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

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

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

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

Очень хорошее объяснение того, как обращаться с GetHashCode (помимо правил Microsoft), приведено в Eric Lipperts (соавтор конструктора С#) в блоге со статьей " Руководства и правила для GetHashCode". Неправильная практика заключается в том, чтобы добавлять гиперссылки сюда (поскольку они могут стать недействительными), но это стоит того, и при условии, что информация выше одной, вероятно, все равно найдет ее в случае потери гиперссылки.

Ответ 5

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

Ответ 6

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