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

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

Ниже приведены основные этапы правильно выстроенного процесса разработки классов настраиваемых атрибутов.

В этом разделе описано каждое из этих действий. В конце приведен пример настраиваемого атрибута.

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

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

[AttributeUsage(AttributeTargets::All, Inherited = false, AllowMultiple = true)]
[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 | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
    Inherits Attribute
    '...
End Class

Свойство Inherited

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

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

[AttributeUsage(AttributeTargets::Method, Inherited = false)]
public ref class YourAttribute : Attribute
{
    //...
};
// 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 ref class MyClass
{
public:
    [MyAttribute]
    [YourAttribute]
    virtual void MyMethod()
    {
        //...
    }
};
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 ref class YourClass : MyClass
{
public:
    // MyMethod will have MyAttribute but not YourAttribute.
    virtual void MyMethod() override
    {
        //...
    }

};
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 имеет значение falseпо умолчаниюAllowMultiple, а YourAttribute значение :true

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

[AttributeUsage(AttributeTargets::Method, AllowMultiple = true)]
public ref class YourAttribute : Attribute
{
};
//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 ref class MyClass
{
public:
    // This produces an error.
    // Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    void MyMethod()
    {
        //...
    }

    // This is valid.
    [YourAttribute]
    [YourAttribute]
    void YourMethod()
    {
        //...
    }
};
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 ref class MyAttribute : Attribute
{
    // . . .
};
[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.

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

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

MyAttribute(bool myvalue)
{
    this->myvalue = myvalue;
}
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 ref class SomeClass
{
    //...
};
// One required (positional) parameter is applied.
[MyAttribute(false)]
public ref class SomeOtherClass
{
    //...
};
// 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 В следующем примере кода показано, как реализовать свойство в атрибуте:

property bool MyProperty
{
    bool get() {return this->myvalue;}
    void set(bool value) {this->myvalue = value;}
}
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 ref class DeveloperAttribute : Attribute
{
    // Private fields.
private:
    String^ name;
    String^ level;
    bool reviewed;

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

    DeveloperAttribute(String^ name, String^ level)
    {
        this->name = name;
        this->level = level;
        this->reviewed = false;
    }

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

    virtual property String^ Name
    {
        String^ get() {return name;}
    }

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

    virtual property String^ Level
    {
        String^ get() {return level;}
    }

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

    virtual property bool Reviewed
    {
        bool get() {return reviewed;}
        void set(bool value) {reviewed = value;}
    }
};
[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)]
<Developer("Joan Smith", "1")>

-or-

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

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

См. также