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


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

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

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

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

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

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

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

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

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

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

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

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

Член AttributeTargets

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

Также можно передать несколько экземпляров AttributeTargets. В следующем фрагменте кода разрешается применение настраиваемого атрибута к любому классу или методу.

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

Свойство Inherited

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

' This defaults to Inherited = true.
Public Class MyAttribute
    Inherits Attribute
    '...
End Class

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

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class YourAttribute : Attribute
{
    //...
}
// This defaults to Inherited = true.
public ref class MyAttribute : Attribute
{
    //...
};

[AttributeUsage(AttributeTargets::Method, Inherited = false)]
public ref class YourAttribute : Attribute
{
    //...
};

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

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

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

Public Class YourClass
    Inherits MeClass
    ' MyMethod will have MyAttribute but not YourAttribute.
    Public Overrides Sub MyMethod()
        '...
    End Sub

End Class
public class YourClass : MyClass
{
    // MyMethod will have MyAttribute but not YourAttribute.
    public override void MyMethod()
    {
        //...
    }

}
public ref class YourClass : MyClass
{
public:
    // MyMethod will have MyAttribute but not YourAttribute.
    virtual void MyMethod() override
    {
        //...
    }

};

Свойство AllowMultiple

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

В следующем примере кода свойство AllowMultiple атрибута MyAttribute имеет значение по умолчанию, false, в то время как для атрибута YourAttribute это свойство имеет значение true.

' This defaults to AllowMultiple = false.
Public Class MyAttribute
    Inherits Attribute
End Class

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

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
//This defaults to AllowMultiple = false.
public ref class MyAttribute : Attribute
{
};

[AttributeUsage(AttributeTargets::Method, AllowMultiple = true)]
public ref class YourAttribute : Attribute
{
};

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

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
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 ref class MyClass
{
public:
    // This produces an error.
    // Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    void MyMethod()
    {
        //...
    }

    // This is valid.
    [YourAttribute]
    [YourAttribute]
    void YourMethod()
    {
        //...
    }
};

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

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

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

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

В этом определении атрибута демонстрируется следующее:

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

  • В соответствии с общепринятой практикой имя класса атрибута должно завершаться словом Attribute. Это условие не обязательно, но полезно для повышения удобства чтения. При применении атрибута использование слова "Attribute" не обязательно.

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

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

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

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

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

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

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

' 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
// 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 ref class SomeClass
{
    //...
};
// One required (positional) parameter is applied.
[MyAttribute(false)]
public ref class SomeOtherClass
{
    //...
};

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

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

Public Property MyProperty As Boolean
    Get
       Return Me.myvalue
    End Get
    Set
        Me.myvalue = Value
    End Set
End Property
public bool MyProperty
{
    get {return this.myvalue;}
    set {this.myvalue = value;}
}
property bool MyProperty
{
    bool get() {return this->myvalue;}
    void set(bool value) {this->myvalue = 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
[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 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;}
    }
};

Этот атрибут можно применять с помощью полного (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)]

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

См. также

Ссылки

System.Attribute

AttributeUsageAttribute

Основные понятия

Расширение метаданных с помощью атрибутов