Довольно просто. Я реализую интерфейс, но есть одно свойство, которое не нужно для этого класса и, фактически, не должно использоваться. Моей первоначальной идеей было сделать что-то вроде:
int IFoo.Bar
{
get { raise new NotImplementedException(); }
}
Я полагаю, что в этом нет ничего плохого, но это не кажется «правильным». Кто-нибудь еще сталкивался с подобной ситуацией раньше? Если да, то как ты к этому подошел?
c#
design
interfaces
implementations
Крис Пратт
источник
источник
Ответы:
Это классический пример того, как люди решают нарушить принцип замещения Лискова. Я настоятельно не одобряю это, но поощрил бы возможно другое решение:
Если первое относится к вам, просто не реализуйте интерфейс для этого класса. Думайте об этом как о электрической розетке, где отверстие для заземления не нужно, чтобы оно на самом деле не прикреплялось к земле. Вы ничего не подключаете с нуля и ничего страшного! Но как только вы воспользуетесь чем-то, что нуждается в заземлении, вас может ожидать впечатляющий провал. Лучше не пробивать ложную дыру. Поэтому, если ваш класс на самом деле не делает то, что задумал интерфейс, не реализуйте интерфейс.
Вот несколько быстрых кусочков из википедии:
Принцип замещения Лискова можно просто сформулировать так: «Не усиливайте предварительные условия и не ослабляйте постусловия».
Для семантической совместимости и взаимозаменяемости между различными реализациями одних и тех же контрактов - вам нужно, чтобы все они придерживались одного и того же поведения.
Принцип сегрегации интерфейса говорит о том, что интерфейсы должны быть разделены на связные наборы, так что вам не требуется интерфейс, который делает много разных вещей, когда вам нужен только один объект. Подумайте еще раз об интерфейсе электрической розетки, он также может иметь термостат, но это усложнит установку электрической розетки и может затруднить его использование в целях без нагрева. Подобно электрической розетке с термостатом, большие интерфейсы сложны в реализации и использовании.
источник
Выглядит хорошо для меня, если это ваша ситуация.
Однако мне кажется, что ваш интерфейс (или его использование) не работает, если производный класс на самом деле не реализует все это. Попробуйте разделить этот интерфейс.
Отказ от ответственности: это требует множественного наследования для правильной работы, и я не знаю, поддерживает ли это C #.источник
Я сталкивался с этой ситуацией. На самом деле, как указано в другом месте, в BCL есть такие примеры ... Я попытаюсь привести лучшие примеры и дать некоторое обоснование:
Если у вас уже есть поставляемый интерфейс, который вы сохраняете по причинам совместимости и ...
Интерфейс содержит элементы, которые являются устаревшими или устаревшими. Например
BlockingCollection<T>.ICollection.SyncRoot
(среди прочего), хотяICollection.SyncRoot
и не устарел как таковой, он броситNotSupportedException
.Интерфейс содержит элементы, которые задокументированы как необязательные, и что реализация может выдать исключение. Например, в MSDN по
IEnumerator.Reset
этому поводу сказано:По ошибке дизайна интерфейса, это должно было быть больше чем один интерфейс во-первых. В BCL распространена схема реализации версий контейнеров только для чтения
NotSupportedException
. Я сделал это сам, это то, что ожидается сейчас ... ЯICollection<T>.IsReadOnly
возвращаюсь,true
чтобы вы могли сказать им аппарта. Правильный дизайн должен был бы иметь читаемую версию интерфейса, и тогда полный интерфейс наследуется от этого.Нет лучшего интерфейса для использования. Например, у меня есть класс, который позволяет вам получить доступ к элементам по индексу, проверить, содержит ли он элемент и по какому индексу, имеет ли он какой-либо размер, вы можете скопировать его в массив ... это кажется работой,
IList<T>
но мой класс имеет фиксированный размер и не поддерживает ни добавление, ни удаление, поэтому он работает больше как массив, чем список. Но нетIArray<T>
в BCL .Интерфейс принадлежит API, который портирован на несколько платформ, и при реализации конкретной платформы некоторые его части не поддерживаются. В идеале должен быть какой-то способ его обнаружения, чтобы переносимый код, использующий такой API, мог решать, как называть эти части, или нет, но если вы их вызываете, это абсолютно уместно
NotSupportedException
. Это особенно верно, если это порт для новой платформы, которая не была предусмотрена в оригинальном дизайне.Также подумайте, почему это не поддерживается?
Иногда
InvalidOperationException
это лучший вариант. Например, еще один способ добавить полиморфизм в класс - это иметь различную реализацию внутреннего интерфейса, и ваш код выбирает, какой из них создать, в зависимости от параметров, заданных в конструкторе класса. [Это особенно полезно, если вы знаете, что набор опций фиксирован, и вы не хотите, чтобы сторонние классы вводились путем внедрения зависимостей.] Я сделал это для обратного переноса ThreadLocal, потому что реализация отслеживания и не отслеживания слишком далеко appart, и чтоThreadLocal.Values
бросает на реализацию без отслеживания?InvalidOperationException
даже если это не зависит от состояния объекта. В этом случае я ввел класс сам и знал, что этот метод должен быть реализован просто с помощью исключения.Иногда значение по умолчанию имеет смысл. Например, в
ICollection<T>.IsReadOnly
упомянутом выше смысле имеет смысл просто возвращать «true» или «false» в зависимости от случая. Итак ... какова семантикаIFoo.Bar
? там может быть какое-то разумное значение по умолчанию для возврата.Приложение: если вы контролируете интерфейс (и вам не нужно оставаться с ним для совместимости), не должно быть случая, когда вы должны бросить
NotSupportedException
. Хотя вам, возможно, придется разделить интерфейс на два или более меньших интерфейса, чтобы они подходили для вашего случая, что может привести к «загрязнению» в экстремальных ситуациях.источник
Да, разработчики библиотеки .Net сделали. Класс Dictionary делает то, что вы делаете. Он использует явную реализацию, чтобы эффективно скрыть * некоторые методы IDictionary. Здесь это объясняется лучше , но в итоге, чтобы использовать методы словаря Add, CopyTo или Remove, которые принимают KeyValuePairs, сначала необходимо привести объект к IDictionary.
* Это не «сокрытие» методов в строгом смысле слова «скрыть», как это использует Microsoft . Но я не знаю лучшего термина в этом случае.
источник
Вы всегда можете просто реализовать и вернуть мягкое значение или значение по умолчанию. Ведь это просто собственность. Может возвращать 0 (свойство по умолчанию для int) или любое другое значение, имеющее смысл в вашей реализации (int.MinValue, int.MaxValue, 1, 42 и т. Д.)
Бросок исключения кажется плохой формой.
источник