Speak.Me Учить иностранные слова

C#: атрибуты (Attributes)

Атрибуты позволяют добавлять пользовательскую информацию к метаданным элементов кода: сборкам, типам, членам, возвращаемым значениям и параметрам. Например, атрибуты могут быть использованы для сериализации — процесс преобразования произвольных объектов в определенный формат. В этом случае атрибуты полей могут точно устанавливать как преобразовывать поля. Можно написать собственный атрибут и затем использовать его для добавления дополнительной информации к элементам кода. Эта дополнительная информация будет скомпилирована в метаданные сборки. Позже она может быть извлечена с помощью рефлексии.

Существует три вида атрибутов:

  • атрибуты с побитовым отражением
  • специальные атрибуты (пользовательские, custom)
  • псевдоспециальные атрибуты

Расширяемыми являются только специальные атрибуты.

Атрибуты с побитовым отображением

Атрибуты с побитовым отображением отображаются на выделенные биты в метаданных типа. Большинство ключевых слов модификаторов (public, abstract, sealed и др.) компилируются в атрибуты с побитовым отображением. Эти атрибут эффективны в использовании, т.к. занимают мало памяти в метаданных (как правило один бит) и находятся средой CLR с незначительными затратами ресурсов.

При рефлексии их можно извлечь через специальные свойства типа Type и подклассов MemderInfo, такие как IsPublic, IsAbstract и IsSealed. Свойство Attributes возвращает перечисление флагов, которые описывают большинство из них:

Специальные атрибуты

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

Атрибут объявляется классом, который наследуется от абстрактного класса System.Attribute.

Чтобы добавить атрибут к элементу кода, нужно перед этим элементом указать название атрибута (типа атрибута) в квадратных скобках.

По соглашению названия всех типов атрибутов заканчиваются суффиксом Attribute. C# распознает это и позволяет опускать данный суффикс при присоединении атрибута к элементу кода:

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

Именованный (named) и позиционный (positional) параметры атрибутов

У атрибутов могут быть параметры.

Параметры атрибутов делятся на позиционный и именованные. В примере выше первый аргумент — позиционный атрибут, второй — именованный. Позиционный параметры соответствуют параметрам конструктора класса (типа) атрибута, именованный — публичным полям и свойствам класса (типа) атрибута.

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

Цели атрибутов

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

Псевдоспециальные атрибуты

Псевдоспециальные атрибуты (Serializable, StructLayout, In и Out) выглядят и ведут себя подобно обычным специальным атрибутам, с той лишь разницей, что при компиляции они преобразуются в атрибуты с побитовым отображением.

При рефлексии псевдоспециальные атрибуты могут быть получены с помощью специальных свойств (например, IsSerializable), а также с помощью метода GetCustomAttributes. они возвращаются в виде объекта System.Attribute.

AttributeUsage

AttributeUsage — это атрибут, применяемый к классам атрибутов. Он сообщает компилятору, как должен использоваться целевой атрибут:

Свойство AllowMultiple указывает ,может ли целевой атрибут применяться к одной и той же цели несколько раз. Свойство Inherited указывает, должен ли атрибут, примененный к базовому классу, также применяться к производным классам, а в случае методов — должен ли атрибут, примененный к виртуальному методу, также применяться к переопределенным методам. Свойство ValidOn определяет набор целей (классов, интерфейсов, свойств, методов, параметров и т.д.), к которым может быть присоединен целевой атрибут. Он принимает любую комбинацию значений enum AttributeTargetsAll, Delegate, GenericParameter, ParameterAssembly, Enum, Interface, PropertyClass, Event, Method, ReturnValueConstructor, Field, Module, Struct:

Пользовательские атрибуты

Для создания собственного атрибута нужно:

  • создать класс, производный от System.Attribute или его потомка; по соглашению имя класса должно заканчиваться словом Attribute (но не обязательно)
  • применить к этому классу описанный выше атрибут AttributeUsage
  • если атрибут не требует свойств или аргументов конструктора, на этом его создание завершено
  • добавить в класс один или несколько публичных конструкторовж параметры конструктора определяют позиционные параметры конструктора и становятся обязательными при использовании атрибута
  • объявить публичное свойство или поле для каждого именованного параметра атрибута

Параметры атрибута могут быть экземплярами следующих типов:

  • примитивные типы: bool, byte, chardouble, float, int, long, short или string
  • Type
  • enum
  • одномерный массив любого из указанных выше типов
Использовать атрибут из примера можно так:

Извлечение атрибутов

Существует два способа извлечения атрибутов:

  • вызвать GetCustomAttributes на любом экземпляре Type или MemberInfo
  • вызвать Attribute.GetCustomAttribute или Attribute.GetCustomAttributes

Последние два метода перегружены и способны принимать экземпляры следующих типов (соответствующие всем допустимым целям атрибутов): Type, Assembly, Module, MemberInfo или ParameterInfo.