Поделиться через


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

Для разработки пользовательских атрибутов вам не нужно изучать множество новых концепций. Если вы знакомы с объектно-ориентированным программированием и знаете, как разрабатывать классы, у вас уже есть большая часть необходимых знаний. Пользовательские атрибуты — это традиционные классы, которые прямо или косвенно являются производными от 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 аргумент.

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

// 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)>

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

См. также