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