Прошло много времени, но, тем не менее, я думаю, что все еще необходимо дать правильный ответ на этот вопрос, включая объяснения, почему и как. Лучший ответ на данный момент - это тот, который цитирует MSDN исчерпывающе - не пытайтесь устанавливать свои собственные правила, ребята из MS знали, что они делают.
Но обо всем по порядку:
Указанное в вопросе руководство неверно.
Теперь почему - их два
Сначала почему :
Если хеш-код вычисляется таким образом, что он не изменяется в течение жизни объекта, даже если сам объект изменяется, то это нарушит контракт на равенство.
Помните:
«Если два объекта сравниваются как равные, метод GetHashCode для каждого объекта должен возвращать одно и то же значение. Однако, если два объекта не сравниваются как равные, методы GetHashCode для двух объектов не должны возвращать разные значения.»
Второе предложение часто неверно истолковывается как «Единственное правило состоит в том, что во время создания объекта хэш-код одинаковых объектов должен быть равен». Не знаю почему, но в этом суть большинства ответов.
Подумайте о двух объектах, содержащих имя, где имя используется в методе equals: «То же имя -> то же самое».
Создать экземпляр A: Имя = Джо
Создать экземпляр B: Имя = Питер
Хэш-код A и Hashcode B, скорее всего, не будут совпадать.
Что теперь произойдет, если имя экземпляра B изменится на Joe?
Согласно руководству из вопроса, хэш-код B не изменится. Результатом этого будет:
A.Equals (B) ==> верно
Но в то же время:
A.GetHashCode () == B.GetHashCode () ==> false.
Но именно это поведение явно запрещено контрактом equals & hashcode.
Второй почему :
Хотя, конечно, верно, что изменения в хеш-коде могут нарушить хешированные списки и другие объекты, использующие хеш-код, обратное также верно. Если не изменить хеш-код, в худшем случае получатся хешированные списки, где все множество различных объектов будут иметь одинаковый хеш-код и, следовательно, находиться в одном и том же хэш-бине - это происходит, когда объекты инициализируются, например, со стандартным значением.
Теперь перейдем к тому, как
Что ж, на первый взгляд, кажется, что есть противоречие - в любом случае код сломается.
Но ни одна из проблем не связана с измененным или неизменным хеш-кодом.
Источник проблем хорошо описан в MSDN:
Из записи хеш-таблицы MSDN:
Ключевые объекты должны быть неизменными до тех пор, пока
так как они используются в качестве ключей в
Hashtable.
Это значит:
Любой объект, который создает хеш-значение, должен изменять хеш-значение, когда объект изменяется, но он не должен - абсолютно не должен - разрешать какие-либо изменения самому себе, когда он используется внутри Hashtable (или любого другого объекта, использующего Hash, конечно).
Сначала как
Самым простым способом, конечно, было бы проектировать неизменяемые объекты только для использования в хеш-таблицах, которые будут создаваться как копии обычных, изменяемых объектов при необходимости.
Внутри неизменяемых объектов совершенно очевидно, что кэшировать хеш-код вполне нормально, поскольку он неизменен.
Второе как
Или присвойте объекту флаг «Вы сейчас хэшированы», убедитесь, что все данные объекта являются частными, проверьте флаг во всех функциях, которые могут изменять данные объектов, и сгенерируйте данные исключения, если изменение не разрешено (т.е. установлен флаг).
Теперь, когда вы помещаете объект в любую область хеширования, убедитесь, что вы установили флаг, а также - сбросили флаг, когда он больше не нужен.
Для простоты использования я бы посоветовал автоматически установить флаг внутри метода «GetHashCode» - так его нельзя забыть. А явный вызов метода «ResetHashFlag» гарантирует, что программисту придется думать, разрешено ли ему изменять данные объектов на данный момент.
Хорошо, что также следует сказать: есть случаи, когда возможно иметь объекты с изменяемыми данными, когда хеш-код, тем не менее, не изменяется, когда данные объектов изменяются, не нарушая контракт equals & hashcode-contract.
Это, однако, требует, чтобы метод equals также не основывался на изменчивых данных.
Итак, если я напишу объект и создаю метод GetHashCode, который вычисляет значение только один раз и сохраняет его внутри объекта, чтобы вернуть его при последующих вызовах, то я снова должен: абсолютно необходимо создать метод Equals, который будет использовать сохраненные значения для сравнения, так что A.Equals (B) никогда не изменится с ложного на истинное. В противном случае договор будет нарушен. Результатом этого обычно будет то, что метод Equals не имеет никакого смысла - это не исходная ссылка equals, но это также не значение равно. Иногда это может быть предполагаемое поведение (то есть записи клиента), но обычно это не так.
Итак, просто измените результат GetHashCode, когда данные объекта изменятся, и если использование объекта внутри хеша с использованием списков или объектов предназначено (или просто возможно), тогда сделайте объект либо неизменным, либо создайте флаг только для чтения, чтобы использовать для времени жизни хешированного списка, содержащего объект.
(Кстати: все это не специфично для C # oder .NET - это характер всех реализаций хеш-таблиц или, в более общем смысле, любого индексированного списка, что идентификационные данные объектов никогда не должны изменяться, пока объект в списке. Неожиданное и непредсказуемое поведение произойдет, если это правило будет нарушено. Где-то могут быть реализации списка, которые отслеживают все элементы в списке и выполняют автоматическую переиндексацию списка - но производительность этих элементов, безусловно, будет ужасной. лучше всего.)