Позиционные и именованные параметры

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

Пример:

using System;

[AttributeUsage(AttributeTargets.Class)]
public class HelpAttribute: Attribute
{
public HelpAttribute(string url) { // Positional parameter
...
}

public string Topic { // Named parameter
get {...}
set {...}
}

public string Url {
get {...}
}
}

определяется класс атрибута с именем HelpAttribute, имеющий один позиционный параметр url и один именованный параметр Topic. Хотя свойство Url не статическое и открытое, оно не определяет именованный параметр, так как не является доступным для чтения и записи.

Этот класс атрибута можно использовать следующим образом:

[Help("http://www.mycompany.com/.../Class1.htm")]
class Class1
{
...
}

[Help("http://www.mycompany.com/.../Misc.htm", Topic = "Class2")]
class Class2
{
...
}

Типы параметров атрибута

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

· один из следующих типов: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort;

· Тип object.

· Тип System.Type.

· перечислимый тип, если и он имеет доступность public, и типы, в которые он вложен (если вложен), также имеют доступность public (§17.2);

· одномерные массивы указанных выше типов.

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

Спецификация атрибута

Спецификация атрибута — это применение предварительно определенного атрибута к объявлению. Атрибут является частью дополнительных декларативных сведений, задаваемых для объявления. Атрибуты могут задаваться в глобальной области видимости (чтобы задать атрибуты для содержащей сборки или модуля) и для объявления_типа (§9.6), объявления_члена_класса (§10.1.5), объявления_члена_интерфейса (§13.2), объявления_члена_структуры (§11.2), объявления_члена_перечисления (§14.3), объявления_метода_доступа (§10.7.2), объявления_метода_доступа_события (§10.8.1) и списка_формальных_параметров (§10.6.1).

Атрибуты задаются в разделах атрибутов. Раздел атрибутов состоит из пары квадратных скобок, которые окружают список из одного или более атрибутов, разделенных запятыми. Порядок указания атрибутов в таком списке и порядок размещения разделов, вложенных в одну и ту же программную сущность, не имеет значения. Например, спецификации атрибутов [A][B], [B][A], [A, B] и [B, A] эквивалентны.

global-attributes:
global-attribute-sections

global-attribute-sections:
global-attribute-section
global-attribute-sections global-attribute-section

global-attribute-section:
[ global-attribute-target-specifier attribute-list ]
[ global-attribute-target-specifier attribute-list , ]

global-attribute-target-specifier:
global-attribute-target :

global-attribute-target:
assembly
module

attributes:
attribute-sections

attribute-sections:
attribute-section
attribute-sections attribute-section

attribute-section:
[ attribute-target-specifieropt attribute-list ]
[ attribute-target-specifieropt attribute-list , ]

attribute-target-specifier:
attribute-target :

attribute-target:
field
event
method
param
property
return
type

attribute-list:
attribute
attribute-list , attribute

attribute:
attribute-name attribute-argumentsopt

attribute-name:
type-name

attribute-arguments:
( positional-argument-listopt )
( positional-argument-list , named-argument-list )
( named-argument-list )

positional-argument-list:
positional-argument
positional-argument-list , positional-argument

positional-argument:
argument-nameopt attribute-argument-expression

named-argument-list:
named-argument
named-argument-list , named-argument

named-argument:
identifier = attribute-argument-expression

attribute-argument-expression:
expression

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

Имя_атрибута определяет класс атрибута. Если имя_атрибута имеет форму имени_типа, это имя должно ссылаться на класс атрибута. Иначе возникает ошибка времени компиляции. Пример:

class Class1 {}

[Class1] class Class2 {} // Error

В результате возникает ошибка времени компиляции, так как предпринята попытка использовать Class1 в качестве класса атрибута, хотя Class1 не является классом атрибута.

Определенные контексты разрешают спецификацию атрибута более чем для одного целевого объекта. Программа может явно задать целевой объект включением описателя_целевого_объекта_атрибута. Если атрибут размещен на глобальном уровне, требуется глобальный_описатель_целевого_объекта_атрибута. Во всех остальных расположениях применяется обоснованное значение по умолчанию, но описатель__целевого_объекта_атрибута может использоваться для подтверждения или переопределения значения по умолчанию в определенных неоднозначных ситуациях (или просто для подтверждения значения по умолчанию в не вызывающих сомнения случаях). Таким образом, описатели_целевого_объекта_атрибута обычно могут быть опущены, кроме случаев их использования на глобальном уровне. В случае потенциально неоднозначных контекстов решение принимается следующим образом:

· атрибут, заданный в глобальной области видимости, может применяться либо к конечной сборке, либо к конечному модулю. Для этого контекста нет значения по умолчанию, поэтому в этом контексте всегда требуется описатель_целевого_объекта_атрибута. Наличие описателя__целевого_объекта_атрибута assembly указывает, что атрибут применяется к конечной сборке; наличие описателя_целевого_объекта_атрибута module указывает, что атрибут применяется к конечному модулю;

· атрибут, заданный в объявлении делегата, может применяться либо к объявляемому делегату, либо к его возвращаемому значению. При отсутствии описателя_целевого_объекта_атрибута атрибут применяется к делегату. Наличие описателя_целевого_объекта_атрибута type указывает, что атрибут применяется к делегату; наличие описателя_целевого_объекта_атрибута return указывает, что атрибут применяется к возвращаемому значению;

· атрибут, заданный в объявлении метода, может применяться либо к объявляемому методу, либо к его возвращаемому значению. При отсутствии описателя_целевого_объекта_атрибута атрибут применяется к методу. Наличие описателя_целевого_объекта_атрибута method указывает, что атрибут применяется к методу; наличие описателя_целевого_объекта_атрибута return указывает, что атрибут применяется к возвращаемому значению;

· атрибут, заданный в объявлении оператора, может применяться либо к объявляемому оператору, либо к его возвращаемому значению. При отсутствии описателя_целевого_объекта_атрибута атрибут применяется к оператору. Наличие описателя_целевого_объекта_атрибута method указывает, что атрибут применяется к оператору; наличие описателя_целевого_объекта_атрибута return указывает, что атрибут применяется к возвращаемому значению.

· атрибут, заданный в объявлении события, в котором опущены методы доступа к событиям, может применяться к объявляемому событию, к связанному полю (если событие не является абстрактным) или к связанным методам add и remove. При отсутствии описателя_целевого_объекта_атрибута атрибут применяется к событию. Наличие описателя_целевого_объекта_атрибута event указывает, что атрибут применяется к событию; наличие описателя_целевого_объекта_атрибута field указывает, что атрибут применяется к полю; а наличие описателя_целевого_объекта_атрибута method указывает, что атрибут применяется к методам.

· атрибут, заданный в объявлении метода доступа get для объявления свойства или индексатора, может применяться либо к связанному методу, либо к его возвращаемому значению. При отсутствии описателя_целевого_объекта_атрибута атрибут применяется к методу. Наличие описателя_целевого_объекта_атрибута method указывает, что атрибут применяется к методу; наличие описателя_целевого_объекта_атрибута return указывает, что атрибут применяется к возвращаемому значению;

· атрибут, указанный в объявлении метода доступа set для объявления свойства или индексатора, может применяться либо к связанному методу, либо к его одиночному неявному параметру. При отсутствии описателя_целевого_объекта_атрибута атрибут применяется к методу. Наличие описателя_целевого_объекта_атрибута method указывает, что атрибут применяется к методу; наличие описателя_целевого_объекта_атрибута param указывает, что атрибут применяется к параметру; наличие описателя_целевого_объекта_атрибута return указывает, что атрибут применяется к возвращаемому значению.

· атрибут, заданный в объявлении метода доступа add или remove для объявления события, может применяться или к связанному методу, или к его одиночному параметру. При отсутствии описателя_целевого_объекта_атрибута атрибут применяется к методу. Наличие описателя_целевого_объекта_атрибута method указывает, что атрибут применяется к методу; наличие описателя_целевого_объекта_атрибута param указывает, что атрибут применяется к параметру; наличие описателя_целевого_объекта_атрибута return указывает, что атрибут применяется к возвращаемому значению.

В других контекстах включение описателя_целевого_объекта_атрибута разрешено, но излишне. Например, объявление класса может или включать, или опускать описатель type:

[type: Author("Brian Kernighan")]
class Class1 {}

[Author("Dennis Ritchie")]
class Class2 {}

Является ошибкой задание недопустимого описателя_целевого_объекта_атрибута. Например, описатель param нельзя использовать в объявлении класса:

[param: Author("Brian Kernighan")] // Error
class Class1 {}

Классы атрибутов принято именовать с помощью суффикса Attribute. Имя_атрибута в форме имени_типа может или включать, или опускать этот суффикс. Если обнаружен класс атрибута и с этим суффиксом, и без суффикса, эта неоднозначность приводит к ошибке времени компиляции. Если имя_атрибута записывается таким образом, что его самый правый идентификатор является буквальным идентификатором (§2.4.2), то сопоставляется только атрибут без суффикса, позволяя таким образом разрешить неоднозначность. Пример:

using System;

[AttributeUsage(AttributeTargets.All)]
public class X: Attribute
{}

[AttributeUsage(AttributeTargets.All)]
public class XAttribute: Attribute
{}

[X] // Error: ambiguity
class Class1 {}

[XAttribute] // Refers to XAttribute
class Class2 {}

[@X] // Refers to X
class Class3 {}

[@XAttribute] // Refers to XAttribute
class Class4 {}

показаны два класса атрибутов с именами X и XAttribute. Атрибут [X] является неоднозначным, так как он может ссылаться на X или на XAttribute. Использование буквального идентификатора позволяет указать точное намерение в таких редких случаях. Атрибут [XAttribute] не является неоднозначным (хотя он мог бы им быть, если бы имелся класс атрибута с именем XAttributeAttribute). Если удаляется объявление для класса X, то оба атрибута ссылаются на класс атрибута с именем XAttribute, как это показано в следующем примере:

using System;

[AttributeUsage(AttributeTargets.All)]
public class XAttribute: Attribute
{}

[X] // Refers to XAttribute
class Class1 {}

[XAttribute] // Refers to XAttribute
class Class2 {}

[@X] // Error: no attribute named "X"
class Class3 {}

Использование класса атрибута одноразового использования более одного раза для одной и той же сущности является ошибкой времени компиляции. Пример:

using System;

[AttributeUsage(AttributeTargets.Class)]
public class HelpStringAttribute: Attribute
{
string value;

public HelpStringAttribute(string value) {
this.value = value;
}

public string Value {
get {...}
}
}

[HelpString("Description of Class1")]
[HelpString("Another description of Class1")]
public class Class1 {}

В результате возникает ошибка времени компиляции, так как предпринимается попытка использования HelpString, который является классом атрибута одноразового использования, более одного раза в объявлении Class1.

Выражение E является выражением_аргумента_атрибута, если верны все следующие утверждения:

· тип E — это тип параметра атрибута (§17.1.3);

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

o постоянное значение;

o Объект System.Type.

o Одномерный массив выражения_аргумента_атрибута.

Пример:

using System;

[AttributeUsage(AttributeTargets.Class)]
public class TestAttribute: Attribute
{
public int P1 {
get {...}
set {...}
}

public Type P2 {
get {...}
set {...}
}

public object P3 {
get {...}
set {...}
}
}

[Test(P1 = 1234, P3 = new int[] {1, 3, 5}, P2 = typeof(float))]
class MyClass {}

Выражение_typeof (§7.6.11), используемое в качестве выражения аргумента атрибута, может ссылаться на неуниверсальный тип, закрытый сконструированный тип или на несвязанный универсальный тип, но не может ссылаться на открытый тип. Это обеспечивает возможность разрешения выражения во время компиляции.

class A: Attribute
{
public A(Type t) {...}
}

class G<T>
{
[A(typeof(T))] T t; // Error, open type in attribute
}

class X
{
[A(typeof(List<int>))] int x; // Ok, closed constructed type
[A(typeof(List<>))] int y; // Ok, unbound generic type
}

Экземпляры атрибутов

Экземпляр атрибута — это экземпляр, представляющий атрибут во время выполнения. Атрибут определяется с помощью класса атрибута, позиционных аргументов и именованных аргументов. Экземпляр атрибута — это экземпляр класса атрибута, который инициализируется с помощью позиционных и именованных аргументов.

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

Компиляция атрибута

Компиляция атрибута с классом атрибута T, списком_позиционных_аргументов P и списком_именованных_аргументов N состоит из следующих шагов:

· выполнение шагов обработки времени компиляции для компиляции выражения_создания_объекта вида new T(P). Результатом этих шагов является или ошибка времени компиляции, или определение конструктора экземпляров C для T, который может быть вызван во время выполнения;

· если у C нет доступности public, возникает ошибка времени компиляции;

· для каждого именованного аргумента Arg в N:

o разрешение Name быть идентификатором именованного аргумента Arg;

o Name должен идентифицировать нестатическое открытое с доступом для чтения и записи поле или свойство в T. Если в T нет такого поля или свойства, возникает ошибка времени компиляции.

· Сохранение следующих сведений для создания экземпляра атрибута во время выполнения: класс атрибута T, конструктор экземпляров C в T, список_позиционных_аргументов P и список_именованных_аргументов N.

Наши рекомендации