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