Является ли Object.GetHashCode () уникальным для ссылки или значения? - PullRequest
24 голосов
/ 29 августа 2008

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

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

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

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

Ответы [ 6 ]

29 голосов
/ 29 августа 2008

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

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

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

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

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();
}
5 голосов
/ 29 августа 2008

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

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

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

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

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

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

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

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

4 голосов
/ 29 августа 2008

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

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

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

1 голос
/ 23 сентября 2015

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

0 голосов
/ 03 декабря 2008

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

0 голосов
/ 29 августа 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...