Минуло багато часу, але, тим не менше, я вважаю, що все-таки необхідно дати правильну відповідь на це питання, включаючи пояснення про те, чому та як. На сьогодні найкраща відповідь - це вичерпне посилання на 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.
Але саме така поведінка явно заборонена рівними та хеш-кодами.
По-друге, чому . Хоча це - звичайно - правда, що зміни в хеш-коді можуть порушити хешовані списки та інші об’єкти, що використовують хеш-код, також є зворотне. Якщо не змінити хеш-код, то в гіршому випадку будуть отримані хешовані списки, де всі багато різних об'єктів матимуть однаковий хеш-код, і тому вони будуть знаходитися в одному хеш-коді - це відбувається, коли об'єкти ініціалізуються зі стандартним значенням, наприклад.
Зараз наближається до хау. Ну, на перший погляд, здається, існує суперечність - в будь-якому випадку, код зламається. Але жодна проблема не походить від зміненого або незмінного хеш-коду.
Джерело проблем добре описано в MSDN:
З запису хеш-таблиці MSDN:
Ключові об'єкти повинні бути незмінними до тих пір, поки вони використовуються як ключі в хеш-таблиці.
Це означає:
Будь-який об'єкт, який створює хешвале, повинен змінити хешвайлу, коли об'єкт змінюється, але він не повинен - абсолютно не повинен - допускати будь-яких змін до себе, коли він використовується всередині хеш-таблиці (або будь-якого іншого об'єкта, що використовує хеш, звичайно) .
По-перше, як найпростіший спосіб, звичайно, був би проектувати незмінні об'єкти лише для використання в хештегах, які будуть створюватися як копії звичайних, змінних об'єктів, коли це буде потрібно. Всередині незмінних об'єктів, очевидно, добре кешувати хеш-код, оскільки він незмінний.
По-друге, як або дати об’єкту прапорець «ви хешуєте зараз», переконатися, що всі дані об’єкта є приватними, перевірити прапорець у всіх функціях, які можуть змінювати дані об’єктів, і викидати дані винятків, якщо зміни не дозволено (тобто встановлено прапор ). Тепер, коли ви поміщаєте об'єкт у будь-яку хешовану область, переконайтеся, що встановили прапор, а також - також зняли прапор, коли він більше не потрібен. Для зручності використання я б порадив встановити прапор автоматично всередині методу "GetHashCode" - таким чином про нього не можна забувати. І явний виклик методу "ResetHashFlag" переконається, що програмісту доведеться думати, чи дозволено чи не можна змінювати дані об'єктів на даний момент.
Гаразд, що слід сказати також: Є випадки, коли можна мати об’єкти із змінними даними, коли хеш-код, тим не менше, не змінюється, коли дані об’єктів змінюються, не порушуючи дорівнює & hashcode-контракт.
Однак це вимагає, щоб метод equals також не базувався на змінних даних. Отже, якщо я пишу об'єкт і створюю метод GetHashCode, який обчислює значення лише один раз і зберігає його всередині об'єкта, щоб повернути його при подальших викликах, то я, знову ж таки: абсолютно повинен, створити метод Equals, який буде використовувати збережені значення для порівняння, так що A.Equals (B) також ніколи не зміниться з false на true. Інакше контракт був би порушений. Результатом цього, як правило, є те, що метод Equals не має жодного сенсу - це не оригінальне посилання дорівнює, але не є рівним і значення. Іноді це може бути передбачувана поведінка (тобто записи клієнтів), але зазвичай це не так.
Отже, просто зробіть зміну результату GetHashCode, коли дані об’єкта змінюються, і якщо використання об’єкта всередині хешу за допомогою списків або об’єктів призначене (або просто можливе), то зробіть об’єкт незмінним або створіть прапор лише для читання, щоб використовувати для час життя хешованого списку, що містить об'єкт.
(До речі: все це не є специфічним для C # oder .NET - це характер усіх реалізацій хеш-таблиць або, загальніше, будь-якого індексованого списку, що ідентифікаційні дані об'єктів ніколи не повинні змінюватися, поки об'єкт знаходиться у списку . Якщо це правило порушено, трапиться несподівана та непередбачувана поведінка. Десь можуть бути реалізації списків, які контролюють усі елементи у списку та здійснюють автоматичне переіндексування списку, але продуктивність цих, безумовно, буде в найкращому випадку жахливою.)