Хорошо, прежде чем вы сойдете с ума из-за того, что в Интернете размещены сотни похожих по звучанию вопросов, я могу заверить вас, что я провел последние несколько часов, читая все и не нашел ответа. на мой вопрос.
Задний план:
По сути, одно из моих крупномасштабных приложений страдало от ситуации, когда некоторые Binding
в свойстве ListBox.SelectedItem
переставали работать или программа аварийно завершала работу после внесения изменений в текущий выбранный элемент. Сначала я задал 'Элемент с таким же ключом уже добавлен'. Исключение при выборе ListBoxItem из кода вопрос здесь, но ответов нет.
У меня не было времени заняться этой проблемой до этой недели, когда мне дали несколько дней, чтобы разобраться с ней. Короче говоря, я выяснил причину проблемы. Это произошло потому, что мои классы типов данных переопределили метод Equals
и, следовательно, метод GetHashCode
.
Теперь для тех из вас, кто не знает об этой проблеме, я обнаружил, что вы можете реализовать метод GetHashCode
только с использованием неизменяемых полей/свойств. Используя отрывок из ответа Харви Квока на сообщение Overriding GetHashCode(), чтобы объяснить это:
Проблема в том, что GetHashCode используется коллекциями Dictionary и HashSet для помещения каждого элемента в корзину. Если хэш-код вычисляется на основе некоторых изменяемых полей, и поля действительно изменяются после помещения объекта в HashSet или Dictionary, объект больше не может быть найден в HashSet или Dictionary.
Таким образом, настоящая проблема была вызвана тем, что я использовал свойства mutable в методах GetHashCode
. Когда пользователи изменяли значения этих свойств в пользовательском интерфейсе, изменялись связанные значения хэш-кода объектов, после чего элементы больше не могли быть найдены в их коллекциях.
Вопрос:
Итак, мой вопрос: как лучше всего справиться с ситуацией, когда мне нужно реализовать метод GetHashCode
в классах без неизменяемых полей? Извините, позвольте мне уточнить, так как этот вопрос задавался раньше.
Ответы в сообщении Переопределение GetHashCode() предполагают, что в таких ситуациях лучше просто вернуть постоянное значение. ... некоторые предлагают вернуть значение 1
, а другие предлагают вернуть простое число. Лично я не вижу никакой разницы между этими предложениями, потому что я бы подумал, что для любого из них будет использоваться только одно ведро.
Кроме того, статья Рекомендации и правила для GetHashCode в блоге Эрика Липперта есть раздел под названием Рекомендация: распределение хэш-кодов должно быть случайным, в котором подчеркиваются подводные камни использования алгоритма, приводящего к использованию недостаточного количества сегментов. Он предупреждает об алгоритмах, которые сокращают количество используемых сегментов и вызывают проблемы с производительностью, когда сегмент становится слишком большим. Конечно, возврат константы попадает в эту категорию.
У меня возникла идея добавить дополнительное поле Guid
ко всем моим классам типов данных (только в C#, а не в базу данных) специально для использования в методе GetHashCode
и только в нем. Итак, я полагаю, что в конце этого длинного вступления мой актуальный вопрос заключается в том, какая реализация лучше? Обобщить:
Резюме:
При переопределении Object.GetHashCode() в классах без неизменяемых полей лучше ли вернуть константу из метода GetHashCode
или создать дополнительное поле readonly
для каждого класса, которое будет использоваться исключительно в методе GetHashCode
? Если мне нужно добавить новое поле, какого типа оно должно быть и не следует ли включать его в метод Equals
?
Хотя я рад получить ответы от любого, я действительно надеюсь получить ответы от продвинутых разработчиков, хорошо разбирающихся в этом вопросе.
Guid
, и использовать его для получения хэш-кода. Я бы лично старался не добавлятьGuid
к типу только для использования в словаре. Опять же, в качестве альтернативы вы можете использовать что-то вроде карты идентификаторов для ключа для объектов на основе отдельного неизменяемого идентификатора (опять же, вероятно,Guid
, поэтому в результате получается тот же результат). Или, опять же, не меняйте элементы в словаре. Исключайте их, удаляйте, изменяйте, добавляйте заново. - person Adam Houldsworth   schedule 31.10.2013Equals
для уведомления об изменениях в приложении. Под уведомлением об изменении я имею в виду, что у меня есть свойствоHasChanges
, которое использует его следующим образом:return originalState != null && !this.Equals(originalState);
. Если бы мне пришлось удалить реализацииEquals
иGetHashCode
, разве мне не пришлось бы реализовыватьIEqualityComparer<T>
для каждого класса типов данных? У меня около 100 или больше, так что, может быть, я мог бы сделать это в моем следующем проекте. - person Sheridan   schedule 31.10.2013Equals
иGetHashCode
? На страницеIEquatable<T>
Interface в MSDN написано Это должно быть реализован для любого объекта, который может храниться в универсальной коллекции, а затем вIEquatable<T>.Equals
Method говорится: Если вы реализуете Equals, вам также следует переопределить реализации базового класса Object.Equals(Object) и GetHashCode, чтобы их поведение соответствовало поведению IEquatable‹T. ›. - person Sheridan   schedule 02.11.2013