Неопровержимые доводы для Использования интерфейсов маркера вместо атрибутов

Это было обсуждено прежде на Переполнении стека, что мы должны предпочесть атрибуты интерфейсам маркера (интерфейсы без любых участников). Статья Interface Design о MSDN утверждает эту рекомендацию также:

Избегайте использования интерфейсов маркера (интерфейсы без участников).

Пользовательские атрибуты позволяют отмечать тип. Для получения дополнительной информации о пользовательских атрибутах, посмотрите Пользовательские атрибуты Записи. Пользовательские атрибуты предпочтены, когда можно задержать проверку атрибут, пока код не выполняется. Если Ваш сценарий требует времени компиляции, проверяя, Вы не можете выполнить эту инструкцию.

Существует даже правило FxCop осуществить эту рекомендацию:

Избегайте пустых интерфейсов

Интерфейсы определяют участников, которые предоставляют контракт на использование или поведение. Функциональность, описанная интерфейсом, может быть принята любым типом, независимо от того, где тип появляется в иерархии наследования. Тип реализует интерфейс путем обеспечения реализаций для участников интерфейса. Пустой интерфейс не определяет участников, и как таковой, не определяет контракт, который может быть реализован.

Если Ваш дизайн включает пустые интерфейсы, которые типы, как ожидают, реализуют, Вы, вероятно, используете интерфейс в качестве маркера или способ определить группу типов. Если эта идентификация произойдет во времени выполнения, корректный способ выполнить, это должно использовать пользовательский атрибут. Используйте присутствие или отсутствие атрибута или свойства атрибута, для идентификации целевых типов. Если идентификация должна произойти во время компиляции, то использование пустого интерфейса приемлемо.

В статье говорится только одна причина, что Вы могли бы проигнорировать предупреждение: когда Вам нужна идентификация времени компиляции для типов. (Это согласовывается со статьей Interface Design).

Безопасно исключить предупреждение из этого правила, если интерфейс используется для идентификации ряда типов во время компиляции.

Здесь прибывает фактический вопрос: Microsoft не соответствовала их собственной рекомендации в дизайне Библиотеки классов Платформы (по крайней мере, в паре случаев): интерфейс IRequiresSessionState и интерфейс IReadOnlySessionState. Эти интерфейсы используются платформой ASP.NET, чтобы проверить, должна ли она разрешить состояние сеанса для определенного обработчика или нет. Очевидно, это не используется для идентификации времени компиляции типов. Почему они не сделали этого? Я могу думать о двух потенциальных причинах:

  1. Микрооптимизация: Проверка, реализует ли объект интерфейс (obj is IReadOnlySessionState) быстрее, чем использование отражения для проверки на атрибут (type.IsDefined(typeof(SessionStateAttribute), true)). Различие незначительно большую часть времени, но оно могло бы на самом деле иметь значение для критического по отношению к производительности пути выполнения кода во времени выполнения ASP.NET. Однако существуют обходные решения, которые они, возможно, использовали как кэширование результата для каждого типа обработчика. Интересная вещь состоит в том, что веб-сервисы ASMX (которые подвергаются подобным рабочим характеристикам) на самом деле используют EnableSession свойство WebMethod атрибут с этой целью.

  2. Интерфейсы реализации более вероятно, будут, потенциально поддерживаться, чем украшение типов с атрибутами сторонними языками.NET. Так как ASP.NET разработан, чтобы быть агностиком языка, и ASP.NET генерирует код для типов (возможно на стороннем языке с помощью CodeDom), которые реализуют упомянутые интерфейсы на основе EnableSessionState атрибут <%@ Page %> директива, могло бы иметь больше смысла использовать интерфейсы вместо атрибутов.

Что убедительные причины состоят в том, чтобы использовать интерфейсы маркера вместо атрибутов?

Это просто (преждевременный?) оптимизация или крошечная ошибка в дизайне платформы? (Они думают, что отражение является "крупным монстром красными глазами"?) Мысли?

54
задан Community 23 May 2017 в 01:47
поделиться

4 ответа

Microsoft не соответствовала рекомендациям, когда они сделали .NET 1.0, потому что руководство развивалось вместе с рамками, а некоторые правила, которые они не узнали, пока не стало слишком поздно Измените API.

IIRC, примеры, которые вы упоминаете, принадлежат BCL 1.0, так что это объяснило бы это.

Это объясняется в руководящих принципов структуры рамок .


Это сказано, что книгу также замечает, что «[A] тестирование Ttribute - это намного дороже, чем проверка типа» (в боковой панели Rico Mariani).

Он говорит, что иногда вам нужен интерфейс маркера для проверки времени компиляции, что невозможно с атрибутом. Тем не менее, я нахожу пример, приведенный в книге (стр. 88) неубедительным, поэтому я не буду повторять это здесь.

6
ответ дан 7 November 2019 в 08:11
поделиться

Я решительно сторонник маркерных интерфейсов. Мне никогда не нравились атрибуты. Я вижу в них некую мета-информацию для классов и членов, предназначенную, например, для отладчиков. Подобно Исключениям, они не должны влиять на нормальную логику обработки, по моему самому скромному мнению.

4
ответ дан 7 November 2019 в 08:11
поделиться

Я обычно избегаю «интерфейсов маркеров», потому что они не позволяют вам Unmark полученный тип. Но в стороне, вот некоторые конкретные случаи, которые я видел, где интерфейсы маркеров будут предпочтительны встроенной мета-данной поддержке:

  1. разумные ситуации, чувствительные к производительности.
  2. Совместимость с языками, которые не поддерживают аннотацию или атрибуты.
  3. Любой контекст, где заинтересованный код может не иметь доступа к метаданным.
  4. Поддержка общих ограничений и общей дисперсии (типично коллекций).
14
ответ дан 7 November 2019 в 08:11
поделиться

Для универсального типа вы можете захотеть использовать тот же универсальный параметр в интерфейсе маркера. Это недостижимо с помощью атрибута:

interface MyInterface<T> {}

class MyClass<T, U> : MyInterface<U> {}

class OtherClass<T, U> : MyInterface<IDictionary<U, T>> {}

Этот вид интерфейса может быть полезен для связывания одного типа с другим.

Другой хороший вариант использования интерфейса маркера - это когда вы хотите создать миксин типа :

interface MyMixin {}

static class MyMixinMethods {
  public static void Method(this MyMixin self) {}
}

class MyClass : MyMixin {
}

Ациклический шаблон посетителя также использует их. Иногда также используется термин «вырожденный интерфейс».

ОБНОВЛЕНИЕ:

Я не знаю, считается ли это, но я использовал их, чтобы отметить классы для пост-компилятора , над которыми будет работать.

10
ответ дан 7 November 2019 в 08:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: