Pisanie atrybutów niestandardowych
Aby zaprojektować atrybuty niestandardowe, nie musisz uczyć się wielu nowych pojęć. Jeśli znasz programowanie obiektowe i wiesz, jak projektować klasy, masz już większość potrzebnej wiedzy. Atrybuty niestandardowe to tradycyjne klasy, które pochodzą bezpośrednio lub pośrednio z System.Attribute klasy. Podobnie jak w przypadku tradycyjnych klas atrybuty niestandardowe zawierają metody, które przechowują i pobierają dane.
Podstawowe kroki prawidłowego projektowania niestandardowych klas atrybutów są następujące:
W tej sekcji opisano każdy z tych kroków i kończy się przykładem atrybutu niestandardowego.
Stosowanie atrybutuUsageAttribute
Deklaracja atrybutu niestandardowego zaczyna się od atrybutu System.AttributeUsageAttribute , który definiuje niektóre kluczowe cechy klasy atrybutów. Można na przykład określić, czy atrybut może być dziedziczony przez inne klasy, czy też elementy, do których można zastosować atrybut. Poniższy fragment kodu pokazuje, jak używać elementu 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
Element AttributeUsageAttribute ma trzy elementy członkowskie, które są ważne podczas tworzenia atrybutów niestandardowych: AttributeTargets, Inherited i AllowMultiple.
Element członkowski AttributeTargets
W poprzednim przykładzie określono, AttributeTargets.All wskazując, że ten atrybut można zastosować do wszystkich elementów programu. Alternatywnie można określić AttributeTargets.Class, wskazując, że atrybut można zastosować tylko do klasy lub AttributeTargets.Method, wskazując, że atrybut można zastosować tylko do metody. Wszystkie elementy programu można oznaczyć jako opis za pomocą atrybutu niestandardowego w ten sposób.
Można również przekazać wiele AttributeTargets wartości. Poniższy fragment kodu określa, że atrybut niestandardowy można zastosować do dowolnej klasy lub metody:
[AttributeUsage(AttributeTargets::Class | AttributeTargets::Method)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
Inherits Attribute
'...
End Class
Właściwość dziedziczona
Właściwość AttributeUsageAttribute.Inherited wskazuje, czy atrybut może być dziedziczony przez klasy, które pochodzą z klas, do których zastosowano atrybut. Ta właściwość przyjmuje flagę (domyślną true
) lub false
. W poniższym przykładzie MyAttribute
ma wartość true
domyślną Inherited , natomiast YourAttribute
ma Inherited wartość 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
Te dwa atrybuty są następnie stosowane do metody w klasie MyClass
bazowej :
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
Na koniec klasa YourClass
jest dziedziczona z klasy MyClass
bazowej . Metoda MyMethod
pokazuje MyAttribute
, ale nie 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
Właściwość AllowMultiple
Właściwość AttributeUsageAttribute.AllowMultiple wskazuje, czy na elemecie może istnieć wiele wystąpień atrybutu. Jeśli ustawiono wartość true
, wiele wystąpień jest dozwolonych. Jeśli ustawiono wartość false
(wartość domyślna), dozwolone jest tylko jedno wystąpienie.
W poniższym przykładzie ma wartość false
domyślną AllowMultiple typu , a parametr YourAttribute
ma wartość true
: MyAttribute
//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
Po zastosowaniu MyAttribute
wielu wystąpień tych atrybutów tworzy błąd kompilatora. W poniższym przykładzie kodu pokazano prawidłowe użycie YourAttribute
elementu i nieprawidłowe użycie elementu 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
Jeśli właściwość AllowMultiple i Inherited właściwość są ustawione na true
, klasa dziedziczona z innej klasy może dziedziczyć atrybut i mieć inne wystąpienie tego samego atrybutu zastosowanego w tej samej klasie podrzędnej. Jeśli AllowMultiple ustawiono false
wartość , wartości wszystkich atrybutów w klasie nadrzędnej zostaną zastąpione przez nowe wystąpienia tego samego atrybutu w klasie podrzędnej.
Deklarowanie klasy atrybutów
Po zastosowaniu AttributeUsageAttributepolecenia rozpocznij definiowanie specyfiki atrybutu. Deklaracja klasy atrybutów wygląda podobnie do deklaracji tradycyjnej klasy, jak pokazano w poniższym kodzie:
[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
Ta definicja atrybutu pokazuje następujące kwestie:
Klasy atrybutów muszą być zadeklarowane jako klasy publiczne.
Zgodnie z konwencją nazwa klasy atrybutów kończy się wyrazem Atrybut. Chociaż nie jest to wymagane, ta konwencja jest zalecana w celu zapewnienia czytelności. Po zastosowaniu atrybutu dołączenie wyrazu Atrybut jest opcjonalne.
Wszystkie klasy atrybutów muszą dziedziczyć bezpośrednio lub pośrednio z System.Attribute klasy.
W programie Microsoft Visual Basic wszystkie niestandardowe klasy atrybutów muszą mieć System.AttributeUsageAttribute atrybut .
Deklarowanie konstruktorów
Podobnie jak w przypadku tradycyjnych klas atrybuty są inicjowane za pomocą konstruktorów. Poniższy fragment kodu ilustruje typowy konstruktor atrybutu. Ten publiczny konstruktor przyjmuje parametr i ustawia zmienną składową równą jej wartości.
MyAttribute(bool myvalue)
{
this->myvalue = myvalue;
}
public MyAttribute(bool myvalue)
{
this.myvalue = myvalue;
}
Public Sub New(myvalue As Boolean)
Me.myvalue = myvalue
End Sub
Konstruktor można przeciążyć, aby uwzględnić różne kombinacje wartości. Jeśli zdefiniujesz również właściwość dla niestandardowej klasy atrybutów, podczas inicjowania atrybutu można użyć kombinacji nazwanych i pozycyjnych parametrów. Zazwyczaj wszystkie wymagane parametry są definiowane jako pozycyjne i wszystkie parametry opcjonalne jako nazwane. W takim przypadku nie można zainicjować atrybutu bez wymaganego parametru. Wszystkie inne parametry są opcjonalne.
Uwaga
W języku Visual Basic konstruktory klasy atrybutów nie powinny używać argumentu ParamArray
.
Poniższy przykład kodu pokazuje, jak można zastosować atrybut używający poprzedniego konstruktora przy użyciu opcjonalnych i wymaganych parametrów. Przyjęto założenie, że atrybut ma jedną wymaganą wartość logiczną i jedną opcjonalną właściwość ciągu.
// 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
Deklarowanie właściwości
Jeśli chcesz zdefiniować nazwany parametr lub łatwo zwrócić wartości przechowywane przez atrybut, zadeklaruj właściwość. Właściwości atrybutu należy zadeklarować jako jednostki publiczne z opisem typu danych, który zostanie zwrócony. Zdefiniuj zmienną, która będzie przechowywać wartość właściwości i skojarzyć ją z metodami get
i set
. W poniższym przykładzie kodu pokazano, jak zaimplementować właściwość w atrybucie:
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
Przykład atrybutu niestandardowego
Ta sekcja zawiera poprzednie informacje i pokazuje, jak zaprojektować atrybut, który dokumentuje informacje o autorze sekcji kodu. Atrybut w tym przykładzie przechowuje nazwę i poziom programisty oraz informację, czy kod został przejrzyszony. Używa trzech zmiennych prywatnych do przechowywania rzeczywistych wartości do zapisania. Każda zmienna jest reprezentowana przez właściwość publiczną, która pobiera i ustawia wartości. Na koniec konstruktor jest definiowany z dwoma wymaganymi parametrami:
[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
Ten atrybut można zastosować przy użyciu pełnej nazwy , DeveloperAttribute
lub przy użyciu skróconej nazwy , Developer
w jeden z następujących sposobów:
[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)>
W pierwszym przykładzie pokazano atrybut zastosowany tylko z wymaganymi nazwanymi parametrami. W drugim przykładzie przedstawiono atrybut zastosowany zarówno z wymaganymi, jak i opcjonalnymi parametrami.