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