Содержание
Атрибуты позволяют добавлять пользовательскую информацию к метаданным элементов кода: сборкам, типам, членам, возвращаемым значениям и параметрам. Например, атрибуты могут быть использованы для сериализации — процесс преобразования произвольных объектов в определенный формат. В этом случае атрибуты полей могут точно устанавливать как преобразовывать поля. Можно написать собственный атрибут и затем использовать его для добавления дополнительной информации к элементам кода. Эта дополнительная информация будет скомпилирована в метаданные сборки. Позже она может быть извлечена с помощью рефлексии.
Существует три вида атрибутов:
- атрибуты с побитовым отражением
- специальные атрибуты (пользовательские, custom)
- псевдоспециальные атрибуты
Расширяемыми являются только специальные атрибуты.
Атрибуты с побитовым отображением
Атрибуты с побитовым отображением отображаются на выделенные биты в метаданных типа. Большинство ключевых слов модификаторов (public
, abstract
, sealed
и др.) компилируются в атрибуты с побитовым отображением. Эти атрибут эффективны в использовании, т.к. занимают мало памяти в метаданных (как правило один бит) и находятся средой CLR с незначительными затратами ресурсов.
При рефлексии их можно извлечь через специальные свойства типа Type
и подклассов MemderInfo
, такие как IsPublic
, IsAbstract
и IsSealed
. Свойство Attributes
возвращает перечисление флагов, которые описывают большинство из них:
1 2 3 4 5 6 7 8 | static void Main() { TypeAttributes ta = typeof (Console).Attributes; MethodAttributes ma = MethodInfo.GetCurrentMethod().Attributes; Console.WriteLine (ta + "\r\n" + ma); } /* AutoLayout, AnsiClass, Class, Public, Abstract, Sealed, BeforeFieldInit PrivateScope, Private, Static, HideBySig */ |
Специальные атрибуты
Специальные атрибуты компилируются в конструкцию, хранимую в главной таблице метаданных типа. Все специальные атрибуты представляют собой подкласс System.Attribute
и в отличие от атрибутов с побитовым отображением являются расширяемыми. Конструкция в метаданных идентифицирует класс атрибута и хранит значения всех позиционных и именованных аргументов, которые были указаны во время применения атрибута.
Атрибут объявляется классом, который наследуется от абстрактного класса System.Attribute
.
1 | public sealed class ObsoleteAttribute : Attribute {...} |
Чтобы добавить атрибут к элементу кода, нужно перед этим элементом указать название атрибута (типа атрибута) в квадратных скобках.
1 2 | [ObsoleteAttribute] public class Foo {...} |
По соглашению названия всех типов атрибутов заканчиваются суффиксом Attribute
. C# распознает это и позволяет опускать данный суффикс при присоединении атрибута к элементу кода:
1 2 | [Obsolete] public class Foo {...} |
Для одного элемента кода можно добавить несколько атрибутов. При этом либо все атрибуты через запятую приводятся внутри одной пары квадратных скобок, либо каждый атрибут указывается в отдельной паре квадратных скобок:
1 2 3 4 | [Serializable, Obsolete, CLSCompliant(false)] public class Bar {...} [Serializable] [Obsolete] [CLSCompliant(false)] public class Bar {...} |
Именованный (named) и позиционный (positional) параметры атрибутов
У атрибутов могут быть параметры.
1 2 | [XmlElement ("Customer", Namespace="http://oreilly.com")] public class CustomerEntity { ... } |
Параметры атрибутов делятся на позиционный и именованные. В примере выше первый аргумент — позиционный атрибут, второй — именованный. Позиционный параметры соответствуют параметрам конструктора класса (типа) атрибута, именованный — публичным полям и свойствам класса (типа) атрибута.
При добавлении атрибута к элементу кода, позиционные атрибуты должны быть указаны, именованный указываются факультативно.
Цели атрибутов
Как правило целью атрибутов является элемент кода, которому они непосредственно предшествуют. В качестве таких элементов обычно выступают типы или члены типов. В этом случае цель атрибутов специально указывать не надо. Также атрибуты можно присоединить к сборке, но в таком случае цель атрибута должна быть указана явно:
1 | [assembly:CLSCompliant(true)] |
Псевдоспециальные атрибуты
Псевдоспециальные атрибуты (Serializable
, StructLayout
, In
и Out
) выглядят и ведут себя подобно обычным специальным атрибутам, с той лишь разницей, что при компиляции они преобразуются в атрибуты с побитовым отображением.
При рефлексии псевдоспециальные атрибуты могут быть получены с помощью специальных свойств (например, IsSerializable
), а также с помощью метода GetCustomAttributes
. они возвращаются в виде объекта System.Attribute
.
AttributeUsage
AttributeUsage
— это атрибут, применяемый к классам атрибутов. Он сообщает компилятору, как должен использоваться целевой атрибут:
1 2 3 4 5 6 7 | public sealed class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute (AttributeTargets validOn); public bool AllowMultiple { get; set; } public bool Inherited { get; set; } public AttributeTargets ValidOn { get; } } |
Свойство AllowMultiple
указывает ,может ли целевой атрибут применяться к одной и той же цели несколько раз. Свойство Inherited
указывает, должен ли атрибут, примененный к базовому классу, также применяться к производным классам, а в случае методов — должен ли атрибут, примененный к виртуальному методу, также применяться к переопределенным методам. Свойство ValidOn
определяет набор целей (классов, интерфейсов, свойств, методов, параметров и т.д.), к которым может быть присоединен целевой атрибут. Он принимает любую комбинацию значений enum AttributeTargets
: All
, Delegate
, GenericParameter
, Parameter
, Assembly
, Enum
, Interface
, Property
, Class
, Event
, Method
, ReturnValue
, Constructor
, Field
, Module
, Struct
:
1 2 3 4 5 6 | [AttributeUsage (AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Class, Inherited = false) ] public sealed class SerializableAttribute : Attribute { } |
Пользовательские атрибуты
Для создания собственного атрибута нужно:
- создать класс, производный от
Sys
tem.Attribute
или его потомка; по соглашению имя класса должно заканчиваться словомAttribute
(но не обязательно) - применить к этому классу описанный выше атрибут
AttributeUsage
- если атрибут не требует свойств или аргументов конструктора, на этом его создание завершено
- добавить в класс один или несколько публичных конструкторовж параметры конструктора определяют позиционные параметры конструктора и становятся обязательными при использовании атрибута
- объявить публичное свойство или поле для каждого именованного параметра атрибута
Параметры атрибута могут быть экземплярами следующих типов:
- примитивные типы:
bool
,byte
,char
,double
,float
,int
,long
,short
илиstring
Type
enum
- одномерный массив любого из указанных выше типов
1 2 3 4 5 6 7 8 9 10 11 | [AttributeUsage (AttributeTargets.Method)] public sealed class TestAttribute : Attribute { public int Repetitions; public string FailureMessage; public TestAttribute () : this (1) { } public TestAttribute (int repetitions) { Repetitions = repetitions; } } |
1 2 3 4 5 6 7 8 9 | class Foo { [Test] public void Method1() { ... } [Test(20)] public void Method2() { ... } [Test(20, FailureMessage="Debugging Time!")] public void Method3() { ... } } |
Извлечение атрибутов
Существует два способа извлечения атрибутов:
- вызвать
GetCustomAttributes
на любом экземпляреType
илиMemberInfo
- вызвать
Attribute.GetCustomAttribute
илиAttribute.GetCustomAttributes
Последние два метода перегружены и способны принимать экземпляры следующих типов (соответствующие всем допустимым целям атрибутов): Type
, Assembly
, Module
, MemberInfo
или ParameterInfo
.
1 2 3 4 5 6 7 8 9 10 11 | foreach (MethodInfo mi in typeof (Foo).GetMethods()) { TestAttribute att = (TestAttribute) Attribute.GetCustomAttribute (mi, typeof (TestAttribute)); if (att != null) Console.WriteLine ( "{0} will be tested; reps={1}; msg={2}", mi.Name, att.Repetitions, att.FailureMessage); } |