Подтвердить что ты не робот

Какова цель интерфейса маркера?

Какова цель интерфейса маркера?

4b9b3361

Ответ 1

Это немного касательная, основанная на ответе "Митч Пшеница".

Как правило, в любое время, когда я вижу, что люди ссылаются на руководящие принципы разработки рамок, я всегда хотел бы упомянуть, что:

Обычно вы обычно игнорируете основные правила проектирования.

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

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

Многие предложения в нем помогут вам сделать то, что:

  • Не может быть самым простым способом реализовать что-то.
  • Может привести к дублированию кода.
  • Может потребоваться дополнительное время выполнения

Структура .net большая, очень большая. Он настолько велик, что было бы совершенно необоснованно предполагать, что у кого-то есть подробные знания обо всех его аспектах. На самом деле гораздо безопаснее предположить, что большинство программистов часто сталкиваются с частями структуры, которые они никогда раньше не использовали.

В этом случае основными задачами дизайнера API являются:

  • Держите вещи в соответствии с остальной частью фреймворка
  • Устранить ненужную сложность в области поверхности API.

Руководства по разработке каркаса побуждают разработчиков создавать код, который выполняет эти цели.

Это означает, что нужно делать такие вещи, как избегать слоев наследования, даже если это означает дублирование кода или вытеснение кода исключения из кода исключения в "точки входа" вместо использования общих помощников (так что трассировки стека имеют больше смысла в отладчике) и много других подобных вещей.

Основная причина, по которой эти рекомендации предполагают использование атрибутов вместо интерфейсов маркеров, заключается в том, что удаление интерфейсов маркеров делает структуру наследования библиотеки классов намного более доступной. Диаграмма классов с 30 типами и 6 уровнями иерархии наследования очень сложная по сравнению с одной с 15 типами и 2 уровнями иерархии.

Если действительно существуют миллионы разработчиков, использующих ваши API, или ваша база кода действительно большая (скажем, более 100 тыс. LOC), то следовать этим рекомендациям может помочь много.

Если 5 миллионов разработчиков потратят 15 минут на изучение API, а не на 60 минут обучения, результат - чистая экономия в 428 человеко-лет. Это много времени.

Большинство проектов, однако, не привлекают миллионы разработчиков, или 100K + LOC. В типичном проекте, скажем, 4 разработчиках и около 50 тыс. Loc, набор предположений сильно отличается. Разработчики в команде будут лучше понимать, как работает код. Это означает, что гораздо лучше использовать оптимизацию для быстрого создания высококачественного кода, а также для уменьшения количества ошибок и усилий, необходимых для внесения изменений.

Расходы на разработку недельного кода, совместимого с картой .net, с 8-часовым написанием кода, который легко изменить и имеет меньше ошибок, может привести к:

  • Поздние проекты
  • Более низкие бонусы
  • Увеличение количества ошибок
  • Больше времени, проведенного в офисе, и меньше времени на пляже, пить маргариты.

Без 4 999 999 других разработчиков, чтобы поглощать затраты, обычно это не стоит.

Например, тестирование интерфейсов маркеров сводится к одному выражению "is" и приводит к уменьшению количества кода, ищущего атрибуты.

Итак, мой совет:

  • Если вы разрабатываете библиотеки классов (или виджеты пользовательского интерфейса), предназначенные для широкого распространения, строго придерживайтесь принципов структуры.
  • Подумайте о том, чтобы принять некоторые из них, если у вас есть более 100K LOC в вашем проекте.
  • В противном случае полностью игнорировать их.

Ответ 2

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

Дизайн интерфейса и . Рекомендации по дизайну NET Type - дизайн интерфейса препятствовать использованию интерфейсов маркеров в пользу использования атрибутов на С#, но, как указывает @Jay Bazuzi, легче проверить интерфейсы маркеров, чем для атрибутов: o is I

Итак, вместо этого:

public interface IFooAssignable {} 

public class FooAssignableAttribute : IFooAssignable 
{
    ...
}

Рекомендации .NET рекомендовали сделать это:

public class FooAssignableAttribute : Attribute 
{
    ...
}

[FooAssignable]
public class Foo 
{    
   ...
} 

Ответ 3

Поскольку каждый другой ответ заявил "их следует избегать", было бы полезно получить объяснение, почему.

Во-первых, почему используются интерфейсы маркеров: они существуют, чтобы позволить коду, использующему объект, который его реализует, чтобы проверить, реализуют ли он указанный интерфейс и по-разному обрабатывают объект, если он это делает.

Проблема с этим подходом заключается в том, что он прерывает инкапсуляцию. Сам объект теперь имеет косвенный контроль над тем, как он будет использоваться извне. Более того, он знает о системе, в которой он будет использоваться. Применяя интерфейс маркера, определение класса предполагает, что он будет использоваться где-нибудь, который проверяет наличие маркера. Он имеет неявные знания об окружающей среде, в которой он используется, и пытается определить, как он должен использоваться. Это противоречит идее инкапсуляции, поскольку она обладает знаниями о реализации части системы, которая существует полностью вне ее собственной сферы.

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

Таким образом, "маркер" представляет собой метаданные о классе. Эти метаданные не используются самим классом и имеют смысл только для (некоторого!) Внешнего клиентского кода, чтобы он мог обрабатывать объект определенным образом. Поскольку он имеет смысл только для кода клиента, метаданные должны быть в коде клиента, а не в API класса.

Разница между "интерфейсом маркера" и нормальным интерфейсом заключается в том, что интерфейс с методами сообщает внешнему миру, как его можно использовать, тогда как пустой интерфейс подразумевает, что он говорит внешнему миру, как его следует использовать.

Ответ 4

Интерфейс маркера - это просто пустой интерфейс. Класс будет реализовывать этот интерфейс как метаданные, которые будут использоваться по какой-либо причине. В С# вы чаще используете атрибуты для разметки класса по тем же причинам, что и интерфейс маркера на других языках.

Ответ 5

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

Предположим, вы хотите определить метод, который ожидает аргумент, тип которого должен быть точно одним из A, B или C. Во многих функционально-первых языках (например, F #), такой тип можно четко определить как:

type Arg = 
    | AArg of A 
    | BArg of B 
    | CArg of C

Однако в OO-первых языках, таких как С#, это невозможно. Единственный способ добиться чего-то подобного здесь - определить интерфейс IArg и "отметить" A, B и C с ним.

Конечно, вы могли бы избежать использования интерфейса маркера, просто приняв тип "объект" в качестве аргумента, но тогда вы потеряете выразительность и некоторую степень безопасности типов.

Типы дискриминационного союза чрезвычайно полезны и существуют на функциональных языках не менее 30 лет. Как ни странно, по сей день все основные языки OO игнорировали эту функцию, хотя она фактически не имеет никакого отношения к функциональному программированию как таковому, но относится к системе типов.

Ответ 6

Интерфейс маркера позволяет отмечать класс таким образом, который будет применяться ко всем классам потомков. "Чистый" интерфейс маркера не будет определять или наследовать что-либо; более полезным типом интерфейсов маркеров может быть тот, который "наследует" другой интерфейс, но не определяет новых членов. Например, если есть интерфейс "IReadableFoo", можно также определить интерфейс "IImmutableFoo", который будет вести себя как "Foo", но обещает любому, кто его использует, что ничто не изменит его значение. Процедура, которая принимает IImmutableFoo, сможет использовать ее, поскольку она будет IReadableFoo, но подпрограмма будет принимать только классы, которые были объявлены как реализующие IImmutableFoo.

Я не могу придумать множество применений для "чистых" интерфейсов маркеров. Единственный, о котором я могу думать, - это если EqualityComparer (из T).Default вернет Object.Equals для любого типа, который реализовал IDoNotUseEqualityComparer, даже если этот тип также реализовал IEqualityComparer. Это позволило бы иметь незапечатанный неизменный тип, не нарушая Принципа замещения Лискова: если тип запечатывает все методы, связанные с тестированием равенства, производный тип может добавлять дополнительные поля и иметь возможность их изменчивости, но мутация таких полей не будет использоваться, t видны с использованием любых методов базового типа. Не может быть ужасно иметь незапечатанный неизменяемый класс и либо избегать использования классов EqualityComparer.Default или доверенных классов, чтобы не реализовывать IEqualityComparer, но производный класс, который реализовал IEqualityComparer, мог появляться как изменяемый класс, даже если он рассматривается как базовый класс, объект класса.

Ответ 7

Эти два метода расширения решают большинство проблем, которые Скотт утверждает, поддерживает маркерные интерфейсы по атрибутам:

public static bool HasAttribute<T>(this ICustomAttributeProvider self)
    where T : Attribute
{
    return self.GetCustomAttributes(true).Any(o => o is T);
}

public static bool HasAttribute<T>(this object self)
    where T : Attribute
{
    return self != null && self.GetType().HasAttribute<T>()
}

Теперь у вас есть:

if (o.HasAttribute<FooAssignableAttribute>())
{
    //...
}

против

if (o is IFooAssignable)
{
    //...
}

Я не вижу, как построение API займет в 5 раз больше, чем первый образец по сравнению со вторым, как утверждает Скотт.

Ответ 8

Маркеры - это пустые интерфейсы. Маркер находится либо там, либо нет.

класс Foo: IConfidential

Здесь мы отмечаем, что Foo является конфиденциальным. Не требуется никаких дополнительных дополнительных свойств или атрибутов.

Ответ 9

Маркер-интерфейс - это полный пустой интерфейс, который не имеет элементов/элементов данных/реализации.
При необходимости класс реализует интерфейс маркера, это просто " mark"; означает, что он сообщает JVM, что конкретный класс предназначен для клонирования, чтобы позволить ему клонировать. Этот класс предназначен для сериализации его объектов, поэтому, пожалуйста, разрешите его объекты сериализоваться.