Написание настраиваемых атрибутов

Для разработки пользовательских атрибутов вам не нужно изучать множество новых концепций. Если вы знакомы с объектно-ориентированным программированием и знаете, как разрабатывать классы, у вас уже есть большая часть необходимых знаний. Пользовательские атрибуты — это традиционные классы, которые прямо или косвенно являются производными от System.Attribute класса. Как и традиционные классы, пользовательские атрибуты содержат методы, которые хранят и извлекают данные.

Ниже приведены основные шаги по правильному проектированию пользовательских классов атрибутов:

В этом разделе описывается каждый из этих шагов и завершается примером пользовательского атрибута.

Применение атрибута AttributeUsageAttribute

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

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
<AttributeUsage(AttributeTargets.All, Inherited:=False, AllowMultiple:=True)>
Public Class SomeClass
    Inherits Attribute
    '...
End Class

AttributeUsageAttribute имеет три члена, важные для создания пользовательских атрибутов: AttributeTargets, Inherited, и AllowMultiple.

Член AttributeTargets

В предыдущем примере указывается, AttributeTargets.All что этот атрибут можно применить ко всем элементам программы. Кроме того, можно указать AttributeTargets.Class, указывая, что атрибут может применяться только к классу или AttributeTargets.Method, указывая, что атрибут можно применять только к методу. Все элементы программы можно пометить для описания пользовательским атрибутом таким образом.

Можно также передать несколько AttributeTargets значений. Следующий фрагмент кода указывает, что настраиваемый атрибут можно применить к любому классу или методу:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
    Inherits Attribute
    '...
End Class

Унаследованное свойство

Свойство AttributeUsageAttribute.Inherited указывает, может ли атрибут наследоваться классами, производными от классов, к которым применяется атрибут. Это свойство принимает либо значение true (по умолчанию), либо флаг false. В следующем примере MyAttribute имеет значение по умолчанию Inherited, а true имеет YourAttribute значение Inherited:

// This defaults to Inherited = true.
public class MyAttribute : Attribute
{
    //...
}

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class YourAttribute : Attribute
{
    //...
}
' This defaults to Inherited = true.
Public Class MyAttribute
    Inherits Attribute
    '...
End Class

<AttributeUsage(AttributeTargets.Method, Inherited:=False)>
Public Class YourAttribute
    Inherits Attribute
    '...
End Class

Затем два атрибута применяются к методу в базовом классе MyClass:

public class MyClass
{
    [MyAttribute]
    [YourAttribute]
    public virtual void MyMethod()
    {
        //...
    }
}
Public Class MeClass
    <MyAttribute>
    <YourAttribute>
    Public Overridable Sub MyMethod()
        '...
    End Sub
End Class

Наконец, класс YourClass наследуется от базового класса MyClass. MyMethod Метод показываетMyAttribute, но неYourAttribute:

public class YourClass : MyClass
{
    // MyMethod will have MyAttribute but not YourAttribute.
    public override void MyMethod()
    {
        //...
    }
}
Public Class YourClass
    Inherits MeClass
    ' MyMethod will have MyAttribute but not YourAttribute.
    Public Overrides Sub MyMethod()
        '...
    End Sub

End Class

Свойство AllowMultiple

Свойство AttributeUsageAttribute.AllowMultiple указывает, может ли существовать несколько экземпляров атрибута в элементе. Если задано значение true, допускается несколько экземпляров. Если задано значение false (по умолчанию), допускается только один экземпляр.

В следующем примере MyAttribute имеет значение по умолчанию AllowMultiple, а false имеет значение YourAttribute.

//This defaults to AllowMultiple = false.
public class MyAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
' This defaults to AllowMultiple = false.
Public Class MyAttribute
    Inherits Attribute
End Class

<AttributeUsage(AttributeTargets.Method, AllowMultiple:=true)>
Public Class YourAttribute
    Inherits Attribute
End Class

При применении MyAttribute нескольких экземпляров этих атрибутов возникает ошибка компилятора. В следующем примере кода показано допустимое использование и недопустимое использование YourAttributeMyAttribute:

public class MyClass
{
    // This produces an error.
    // Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    public void MyMethod()
    {
        //...
    }

    // This is valid.
    [YourAttribute]
    [YourAttribute]
    public void YourMethod()
    {
        //...
    }
}
Public Class MyClass
    ' This produces an error.
    ' Duplicates are not allowed.
    <MyAttribute>
    <MyAttribute>
    Public Sub MyMethod()
        '...
    End Sub

    ' This is valid.
    <YourAttribute>
    <YourAttribute>
    Public Sub YourMethod()
        '...
    End Sub
End Class

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

Объявление класса атрибутов

После применения этого AttributeUsageAttributeпараметра начните определять особенности атрибута. Объявление класса атрибутов похоже на объявление традиционного класса, как показано в следующем коде:

[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
    // . . .
}
<AttributeUsage(AttributeTargets.Method)>
Public Class MyAttribute
    Inherits Attribute
    ' . . .
End Class

Это определение атрибута демонстрирует следующие моменты:

  • Классы атрибутов должны быть объявлены как общедоступные классы.

  • По соглашению имя класса атрибута заканчивается словом Attribute. Хотя это не обязательно, это соглашение рекомендуется для удобочитаемости. При применении атрибута добавление слова Attribute является необязательным.

  • Все классы атрибутов должны наследоваться напрямую или косвенно от System.Attribute класса.

  • В Microsoft Visual Basic все пользовательские классы атрибутов должны иметь атрибут System.AttributeUsageAttribute.

Объявление конструкторов

Как и традиционные классы, атрибуты инициализированы конструкторами. Следующий фрагмент кода иллюстрирует типичный конструктор атрибутов. Этот открытый конструктор принимает параметр и задает переменную-член, равную его значению.

public MyAttribute(bool myvalue)
{
    this.myvalue = myvalue;
}
Public Sub New(myvalue As Boolean)
    Me.myvalue = myvalue
End Sub

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

Замечание

В Visual Basic конструкторы для класса атрибутов не должны использовать аргумент ParamArray.

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

  • Простые типы (ключевое слово C# / ключевое слово Visual Basic / тип среды выполнения .NET):

    C# Visual Basic тип среды выполнения .NET
    bool Boolean Boolean
    byte Byte Byte
    char Char Char
    double Double Double
    float Single Single
    int Integer Int32
    long Long Int64
    short Short Int16
    string String String
  • Type.

  • Типы перечисления, доступные в месте использования атрибутов.

  • В C# object (если значение является одним из допустимых типов аргументов атрибутов или одномерным массивом из них).

  • Одномерные массивы любого из предыдущих типов.

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

Замечание

Типы sbyte, ushort, uint, ulong, decimal, nint и nuint не являются допустимыми типами параметров атрибутов, несмотря на то, что они поддерживают литеральные константы.

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

// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public class SomeClass
{
    //...
}
// One required (positional) parameter is applied.
[MyAttribute(false)]
public class SomeOtherClass
{
    //...
}
' One required (positional) and one optional (named) parameter are applied.
<MyAttribute(false, OptionalParameter:="optional data")>
Public Class SomeClass
    '...
End Class

' One required (positional) parameter is applied.
<MyAttribute(false)>
Public Class SomeOtherClass
    '...
End Class

Объявление свойств

Если вы хотите определить именованный параметр или предоставить простой способ возврата значений, хранящихся в атрибуте, объявите свойство. Свойства атрибута должны объявляться как общедоступные сущности с описанием возвращаемого типа данных. Определите переменную, которая будет содержать значение вашего свойства и связывать её между методами get и set. В следующем примере кода показано, как реализовать свойство в атрибуте:

public bool MyProperty
{
    get {return this.myvalue;}
    set {this.myvalue = value;}
}
Public Property MyProperty As Boolean
    Get
        Return Me.myvalue
    End Get
    Set
        Me.myvalue = Value
    End Set
End Property

Пример пользовательского атрибута

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

[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : Attribute
{
    // Private fields.
    private string name;
    private string level;
    private bool reviewed;

    // This constructor defines two required parameters: name and level.

    public DeveloperAttribute(string name, string level)
    {
        this.name = name;
        this.level = level;
        this.reviewed = false;
    }

    // Define Name property.
    // This is a read-only attribute.

    public virtual string Name
    {
        get {return name;}
    }

    // Define Level property.
    // This is a read-only attribute.

    public virtual string Level
    {
        get {return level;}
    }

    // Define Reviewed property.
    // This is a read/write attribute.

    public virtual bool Reviewed
    {
        get {return reviewed;}
        set {reviewed = value;}
    }
}
<AttributeUsage(AttributeTargets.All)>
Public Class DeveloperAttribute
    Inherits Attribute
    ' Private fields.
    Private myname As String
    Private mylevel As String
    Private myreviewed As Boolean

    ' This constructor defines two required parameters: name and level.

    Public Sub New(name As String, level As String)
        Me.myname = name
        Me.mylevel = level
        Me.myreviewed = False
    End Sub

    ' Define Name property.
    ' This is a read-only attribute.

    Public Overridable ReadOnly Property Name() As String
        Get
            Return myname
        End Get
    End Property

    ' Define Level property.
    ' This is a read-only attribute.

    Public Overridable ReadOnly Property Level() As String
        Get
            Return mylevel
        End Get
    End Property

    ' Define Reviewed property.
    ' This is a read/write attribute.

    Public Overridable Property Reviewed() As Boolean
        Get
            Return myreviewed
        End Get
        Set
            myreviewed = value
        End Set
    End Property
End Class

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

[Developer("Joan Smith", "1")]

-or-

[Developer("Joan Smith", "1", Reviewed = true)]
<Developer("Joan Smith", "1")>

-or-

<Developer("Joan Smith", "1", Reviewed := true)>

В первом примере показан атрибут, примененный только к обязательным именованным параметрам. Во втором примере показан атрибут, применяемый как с обязательными, так и необязательными параметрами.

См. также