Написание настраиваемых атрибутов
Обновлен: Ноябрь 2007
Чтобы создавать собственные настраиваемые атрибуты, совсем необязательно в совершенстве овладевать множеством новых понятий. Если вы имеете представление об объектно-ориентированном программировании и знаете, как создавать классы, то вы обладаете почти всеми нужными знаниями. Настраиваемые атрибуты, в сущности, представляют собой традиционные классы, прямо или косвенно наследующие от System.Attribute. Подобно традиционным классам, настраиваемые атрибуты содержат методы, хранящие и считывающие данные.
Вот основные этапы правильно выстроенного процесса разработки классов настраиваемых атрибутов.
Применение атрибута AttributeUsageAttribute
Объявление класса атрибута
Объявление конструкторов
Объявление свойств
В этом разделе описано каждое из этих действий; завершается он примером настраиваемого атрибута.
Применение атрибута AttributeUsageAttribute
Объявление настраиваемого атрибута начинается с атрибута AttributeUsageAttribute, определяющего некоторые ключевые характеристики класса атрибута. Например, есть возможность определить, может ли атрибут быть унаследован другими классами, или указать элементы языка, к которым может применяться этот атрибут. В следующем фрагменте кода показан пример использования атрибута AttributeUsageAttribute.
[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 | AttributeTargets.Method)]
<AttributeUsage (AttributeTargets.Class Or AttributeTargets.Method)>
Свойство Inherited
Свойство Inherited указывает, может ли атрибут быть унаследован классами, производными от класса, к которому этот атрибут применен. Это свойство принимает значение true (по умолчанию) или false. Например, в следующем коде свойству Inherited атрибута MyAttribute задано значение по умолчанию, true, в то время как свойство Inherited атрибута YourAttribute имеет значение false.
//This defaults to Inherited = true.
public class MyAttribute :Attribute
{
}
[AttributeUsage( Inherited = false)]
public class YourAttribute : Attribute
{
}
<AttributeUsage( AttributeTargets.All, Inherited := True)> Public Class _
MyAttribute
Inherits Attribute
End Class
<AttributeUsage( AttributeTargets.All, Inherited := False)> Public Class _
YourAttribute
Inherits Attribute
End Class
Затем эти два атрибута применяются к методу в базовом классе MyClass.
public class MyClass
{
[MyAttribute]
[YourAttribute]
public virtual void MyMethod()
{
//...
}
}
' In Microsoft Visual Basic, you apply multiple attributes
' by separating them with commas.
Public Class [MyClass]
<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 [MyClass]
'MyMethod will have MyAttribute but not YourAttribute.
Public overrides Sub MyMethod()
'...
End Sub
End Class
Свойство AllowMultiple
Свойство AllowMultiple указывает, можно ли применять к элементу несколько экземпляров атрибута. Если его значение равно true, то существование нескольких экземпляров атрибута разрешено, если же оно равно false (по умолчанию), то можно использовать только один экземпляр.
В следующем примере кода свойство AllowMultiple атрибута MyAttribute имеет значение по умолчанию, false , в то время как для атрибута YourAttribute это свойство имеет значение true.
//This defaults to AllowMultiple = false.
public class MyAttribute :Attribute
{
}
[AttributeUsage(AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
'This defaults to AllowMultiple = false.
<AttributeUsage(AttributeTargets.Method)> Public Class _
MyAttribute
Inherits Attribute
End Class
<AttributeUsage(AttributeTargets.Method, AllowMultiple := True)> Public Class _
YourAttribute
Inherits Attribute
End Class
При применении нескольких экземпляров этих атрибутов атрибут MyAttribute порождает ошибку компилятора. В следующем примере кода показано правильное использование атрибута YourAttribute и неправильное использование атрибута MyAttribute.
public class MyClass
{
//This produces an error.
//Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
public void MyMethod() {
//...
}
//This is valid.
[YourAttribute]
[YourAttribute]
public void YourMethod(){
//...
}
}
' In Microsoft Visual Basic you apply multiple attributes
' by separating them with commas.
Public Class [MyClass]
'This produces an error.
'Duplicates are not allowed.
<MyAttribute, MyAttribute> Public Overridable Sub MyMethod()
'...
End Sub
'This is valid.
<YourAttribute, YourAttribute> Public Sub YourMethod()
'...
End Sub
End Class
Если и свойство AllowMultiple, и свойство Inherited имеют значение true, то класс, наследуемый от другого класса, может наследовать атрибуты и иметь дополнительные экземпляры атрибута, применяемого в этом же дочернем классе. Если значение AllowMultiple равно false, то значения любых атрибутов в родительском классе будут переписаны новыми экземплярами того же самого атрибута в дочернем классе.
Объявление класса атрибута
После применения AttributeUsageAttribute можно начать работу над определением характеристик атрибута. Объявление класса атрибута выглядит аналогично объявлению традиционного класса, что показано в следующем коде.
public class MyAttribute : System.Attribute
{
// . . .
}
' This attribute is only usable with methods
<AttributeUsage(AttributeTargets.Method)> Public Class MyAttribute
Inherits System.Attribute
' . . .
End Class
В этом определении атрибута демонстрируется следующее:
Классы атрибутов должны объявляться как открытые классы.
В соответствии с общепринятой практикой имя класса атрибута должно завершаться словом Attribute. Это условие не обязательно, но полезно для повышения удобства чтения. При применении атрибута использование слова "Attribute" не обязательно.
Все классы атрибутов должны непосредственно или косвенно наследовать от класса System.Attribute.
В Microsoft Visual Basic все классы настраиваемых атрибутов должны иметь атрибут AttributeUsageAttribute.
Объявление конструкторов
Атрибуты инициализируются с помощью конструкторов точно так же, как традиционные классы. В следующем примере кода показан типичный конструктор атрибута. Этот открытый конструктор получает параметр и присваивает его значение внутренней переменной.
public MyAttribute(bool myvalue)
{
this.myvalue = myvalue;
}
Public Sub New(newvalue As Boolean)
Me.myvalue = newvalue
End Sub
Чтобы использовать различные сочетания параметров, можно выполнить перегрузку конструктора. Если для класса атрибута определяется также и свойство, то при инициализации атрибута можно использовать сочетание именованных и позиционных параметров. В обычном случае все обязательные параметры определяются как позиционные, а все необязательные — как именованные. В этом случае атрибут нельзя инициализировать без обязательного параметра. Все прочие параметры являются необязательными. Следует отметить, что в языке Visual Basic конструкторы классов атрибутов не могут использовать аргумент ParamArray.
В следующем примере кода показано, как атрибут, использующий приведенный выше конструктор, можно использовать с обязательными и необязательными параметрами. Предполагается, что этот атрибут имеет один обязательный логический параметр и одно необязательное строковое свойство.
//One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
//One required (positional) parameter is applied.
[MyAttribute(false)]
'One required (positional) and one optional (named) parameter are applied.
<MyAttribute(False, OptionalParameter := "optional data")>
' ...
'One required (positional) parameter is applied.
<MyAttribute(False)>
Объявление свойств
Если необходимо определить именованный параметр или предоставить простой способ получения значений, хранящихся в атрибуте, то можно объявить свойство. Свойства атрибута следует объявлять как открытые сущности с описанием типа данных, которые они возвращают. Определите переменную, которая будет хранить значение свойства, и свяжите ее с методами 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 : System.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 System.Attribute
'Private fields.
Private m_name As String
Private m_level As String
Private m_reviewed As Boolean
'This constructor defines two required parameters: name and level.
Public Sub New(name As String, level As String)
Me.m_name = name
Me.m_level = level
Me.m_reviewed = False
End Sub
'Define Name property.
'This is a read-only attribute.
Public Overridable ReadOnly Property Name() As String
Get
Return m_name
End Get
End Property
'Define Level property.
'This is a read-only attribute.
Public Overridable ReadOnly Property Level() As String
Get
Return m_level
End Get
End Property
'Define Reviewed property.
'This is a read/write attribute.
Public Overridable Property Reviewed() As Boolean
Get
Return m_reviewed
End Get
Set
m_reviewed = value
End Set
End Property
End Class
Этот атрибут можно применять с помощью полного (DeveloperAttribute) либо сокращенного имени (Developer) одним из следующих способов.
[Developer("Joan Smith", "1")]
[Developer("Joan Smith", "1", Reviewed = true)]
<Developer("Joan Smith", "1")>
<Developer("Joan Smith", "1", Reviewed := True)>
В первом примере этот атрибут применяется только с обязательными именованными параметрами, а во втором примере атрибут применяется и с обязательными, и с необязательными параметрами.