Я размышлял об этом в течение некоторого времени: как именно Object.GetHashCode
реализовано в CLR или Java? Контракт для этого метода заключается в том, что если он вызывается в одном экземпляре объекта, он всегда должен возвращать одно и то же значение.
Обратите внимание, что я говорю о реализации GetHashCode() по умолчанию. Производные классы не обязаны переопределять этот метод. Если они не захотят этого делать, они будут по существу иметь ссылочную семантику: равенство равно "равенству указателя" по умолчанию при использовании в хеш-таблицах & c. Это означает, что каким-то образом среда выполнения должна обеспечивать постоянный хэш-код для объекта на протяжении всего срока его службы.
Если машина, на которой я запущена, является 32-разрядной, и если экземпляр объекта никогда не перемещался в памяти, теоретически можно было бы вернуть адрес объекта, переинтерпретированный как Int32. Это было бы хорошо, так как все разные объекты имеют разные адреса и поэтому имеют разные хэш-коды.
Однако этот подход является ошибочным, среди прочего, потому что:
-
если сборщик мусора перемещает объект в памяти, его адрес изменяется, и его хэш-код нарушает контракт, что хэш-код должен быть одинаковым для времени жизни объекта.
-
В 64-битной системе адрес объекта слишком широк, чтобы вписаться в Int32.
-
Поскольку управляемые объекты, как правило, выровнены с некоторой четной мощностью 2, самые младшие биты всегда будут равны нулю. Это может привести к неправильным схемам распределения, когда хэш-коды используются для индексирования в хэш-таблицу.
В .NET a System.Object
состоит из блока синхронизации и дескриптора типа и ничего больше, поэтому хэш-код не может быть кэширован в самом экземпляре. Как-то среда выполнения может обеспечить постоянный хэш-код. Как? И как это делают Java, Mono и другие среды выполнения?