Как указать, что экземпляры некоторых классов не должны использоваться в качестве ключей словаря?

Меня интересует ваше мнение относительно проблемы, которая возникла при реализации небольшой библиотеки для написания интеграционных / системных тестов на C#. Библиотека состоит из 2 частей: тестового API-интерфейса и тестового Runtime API. Разработчик тестов использует API-интерфейс для создания прототипов планов тестирования. Среда выполнения может взять прототип и создать представление прототипа во время выполнения и выполнить его.

Чтобы иметь возможность протестировать класс с именем RuntimeTestPlanBuilder, мне пришлось переопределить метод Equals во всех классах объектной модели времени выполнения. Это привело к ситуации, когда я должен переопределить метод GetHashCode тоже. Реализация GetHashCode для некоторых классов объектной модели во время выполнения была простой. Поскольку некоторые классы в модели времени выполнения являются контейнерами, реализация GetHashCode для них была сложной и даже невозможной (поскольку среда выполнения может добавлять элементы в контейнер, хэш-код не может быть вычислен, например, в O(1)).

В результате я подумал, как запретить пользователям контейнеров помещать их в словарь в качестве ключей. Я выбрал решение переопределить метод GetHashCode и выдать исключение. Я не удовлетворен этим решением, так как оно выйдет из строя только во время выполнения. Кроме того, я немного удивляюсь, почему разработчики.Net Framework решили поместить метод GetHashCode в класс Object. Я думаю, что это решение привело к моей ситуации, так как я не могу пометить свои контейнеры как недопустимые ключи словаря в хорошем смысле.

Лучшим решением (для разработчиков.Net Framework) могло бы быть определение интерфейса с именем IHashable с помощью двух методов: Equals и GetHashCode и принудительное применение интерфейса с ограничением по общему словарному типу Item. Таким образом, во время компиляции мы можем принудительно установить, что ключи реализуют Equals и GetHashCode. Также легко понять, что классы, которые не реализуют этот интерфейс, не должны использоваться в качестве ключей словаря.

У меня такой вопрос: можете ли вы предложить лучшее решение (которое не генерирует исключения из GetHashCode), чтобы указать, что экземпляры некоторых классов не должны использоваться в качестве ключей словаря?

Спасибо

2 ответа

Решение

Объект должен реализовывать пять методов, и GetHashCode является одним из них.
Не может быть уверен, что единственное текущее или будущее использование GetHashCode для словаря ключа.

Единственное правило - два равных объекта должны иметь один и тот же GetHashCode.
Но тот же GetHashCode не означает, что два объекта равны.

Вы можете просто использовать константу (1) для GetHashCode или количество элементов в контейнере.
Или посмотрите этот ответ от Скит

это-это возможно, к зерноуборочному хэшу-кодам-для частно-членов-к-генерировать-а-новый-хэш

Как правило, два объекта должны сообщать о себе как о равных, если они равны и всегда будут эквивалентны; изменяемые объекты, включая изменяемые коллекции, таким образом, как правило, должны считаться равными друг другу. Неизменные коллекции могут реализовать GetHashCode эффективно кэшируя GetHashCode() ценности их членов; Конструкция - это O(N), и независимо от того, вычисляется ли хеш-код при построении объекта или когда он запрашивается впервые, общая стоимость срока его службы будет не более O(N).

Нет причин, по которым у любого класса могут возникнуть проблемы с реализацией хеш-кода. Если класс использует равенство ссылок, по умолчанию GetHashCode идеально. Если он использует изменяемое определение равенства, GetHashCode() Контракт можно выполнить, просто вернув константу. Попытка поместить в словарь несколько экземпляров может привести к низкой производительности, но возможно, что кто-то может использовать словарь, например, с дюжиной элементов, и производительность будет вполне приемлемой для словаря такого размера.

Другие вопросы по тегам