Scrivere attributi personalizzati
Per progettare attributi personalizzati, non è necessario apprendere molti nuovi concetti. Se si ha familiarità con la programmazione orientata agli oggetti e si è in grado di progettare le classi, si ha già gran parte delle conoscenze necessarie. Gli attributi personalizzati sono classi tradizionali che derivano direttamente o indirettamente dalla classe System.Attribute. Analogamente alle classi tradizionali, gli attributi personalizzati contengono metodi che archiviano e recuperano dati.
I passaggi fondamentali per progettare classi di attributi personalizzati sono i seguenti:
Questa sezione illustra ognuno di questi passaggi e termina con esempio di attributo personalizzato.
Applicazione di AttributeUsageAttribute
La dichiarazione di attributo personalizzato inizia con l'attributo System.AttributeUsageAttribute, che definisce alcune delle caratteristiche chiave della classe di attributi. Ad esempio, è possibile specificare se l'attributo può essere ereditato da altre classi o gli elementi a cui può essere applicato l'attributo. Il frammento di codice seguente illustra come usare 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
L'oggetto AttributeUsageAttribute ha tre membri importanti per la creazione di attributi personalizzati: AttributeTargets, Inheritede AllowMultiple.
Membro AttributeTargets
Nell'esempio precedente è specificato AttributeTargets.All, per indicare che questo attributo può essere applicato a tutti gli elementi di programma. In alternativa, è possibile specificare AttributeTargets.Class, a indicare che l'attributo può essere applicato solo a una classe, oppure AttributeTargets.Method, a indicare che l'attributo può essere applicato solo a un metodo. Questa procedura può essere usata per contrassegnare tutti gli elementi di programma per la descrizione mediante un attributo personalizzato.
È anche possibile passare più valori AttributeTargets. Il frammento di codice seguente specifica che un attributo personalizzato può essere applicato a qualsiasi classe o metodo:
[AttributeUsage(AttributeTargets::Class | AttributeTargets::Method)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
Inherits Attribute
'...
End Class
Proprietà Inherited
La proprietà AttributeUsageAttribute.Inherited indica se l'attributo può essere ereditato da classi derivate dalle classi a cui è applicato l'attributo. Questa proprietà accetta un flag true
(valore predefinito) o false
. Nell'esempio seguente MyAttribute
ha un valore predefinito Inherited impostato su true
, mentre YourAttribute
ha un valore Inherited impostato su 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
I due attributi vengono quindi applicati a un metodo nella classe base 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
Infine, la classe YourClass
viene ereditata dalla classe base MyClass
. Il metodo MyMethod
mostra MyAttribute
, ma non 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
Proprietà AllowMultiple
La proprietà AttributeUsageAttribute.AllowMultiple indica se in un elemento possono esistere istanze multiple dell'attributo. Se è impostata su true
, sono consentite più istanze. Se è impostata su false
(impostazione predefinita), è consentita una sola istanza.
Nell'esempio seguente MyAttribute
ha un valore predefinito AllowMultiple impostato su false
, mentre YourAttribute
ha un valore impostato su 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
Quando vengono applicate istanze multiple di questi attributi, MyAttribute
genera un errore del compilatore. L'esempio di codice seguente mostra l'uso valido di YourAttribute
e l'uso non valido di 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
Se le proprietà AllowMultiple e Inherited sono entrambe impostate su true
, una classe ereditata da un'altra classe può ereditare un attributo e determinare l'applicazione di un'altra istanza dello stesso attributo nella stessa classe figlio. Se la proprietà AllowMultiple è impostata su false
, i valori degli attributi nella classe padre verranno sovrascritti dalle nuove istanze dello stesso attributo nella classe figlio.
Dichiarazione della classe di attributi
Dopo aver applicato AttributeUsageAttribute, iniziare a definire le specifiche dell'attributo. La dichiarazione di una classe Attribute è simile alla dichiarazione di una classe tradizionale, come dimostrato dal codice seguente:
[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
Questa definizione di attributo dimostra quanto segue:
Le classi di attributi devono essere dichiarate come classi pubbliche.
Per convenzione, il nome della classe di attributi termina con la parola Attribute. Sebbene non sia obbligatorio, questa convenzione è consigliabile per migliorare la leggibilità. Quando l'attributo è applicato, l'inclusione della parola Attribute è facoltativa.
Tutte le classi Attribute devono ereditare direttamente o indirettamente dalla classe System.Attribute.
In Microsoft Visual Basic tutte le classi di attributi personalizzati devono avere l'attributo System.AttributeUsageAttribute.
Dichiarazione dei costruttori
Proprio come le classi tradizionali, gli attributi vengono inizializzati con i costruttori. Il frammento di codice seguente illustra un tipico costruttore di attributi. Questo costruttore pubblico accetta un parametro e imposta una variabile membro sul valore del parametro.
MyAttribute(bool myvalue)
{
this->myvalue = myvalue;
}
public MyAttribute(bool myvalue)
{
this.myvalue = myvalue;
}
Public Sub New(myvalue As Boolean)
Me.myvalue = myvalue
End Sub
È possibile eseguire l'overload del costruttore per supportare diverse combinazioni di valori. Se si definisce anche una proprietà per la classe di attributi personalizzati, è possibile usare una combinazione di parametri denominati e posizionali quando si inizializza l'attributo. In genere, tutti i parametri obbligatori vengono definiti come posizionali e tutti i parametri facoltativi come denominati In questo caso, l'attributo non può essere inizializzato senza il parametro obbligatorio. Tutti gli altri parametri sono facoltativi.
Nota
In Visual Basic i costruttori per una classe Attribute non devono usare un argomento ParamArray
.
L'esempio di codice seguente illustra come applicare un attributo che usa il costruttore precedente mediante i parametri obbligatori e facoltativi. Presuppone che l'attributo abbia un valore booleano obbligatorio e una proprietà stringa facoltativa.
// 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
Dichiarazione delle proprietà
Se si vuole definire un parametro denominato o fornire un modo semplice per restituire i valori archiviati dall'attributo, dichiarare una proprietà. Le proprietà dell'attributo devono essere dichiarate come entità pubbliche con una descrizione del tipo di dati che verrà restituito. Definire la variabile che conterrà il valore della proprietà e associarla ai metodi get
e set
. L'esempio di codice seguente illustra come implementare una proprietà nell'attributo:
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
esempio di attributo personalizzato
Questa sezione include le informazioni precedenti e illustra come progettare un attributo che documenta le informazioni sull'autore di una sezione di codice. L'attributo in questo esempio archivia il nome e il livello del programmatore e specifica se il codice è stato rivisto. Usa tre variabili private per archiviare i valori effettivi da salvare. Ogni variabile è rappresentata da una proprietà pubblica che ottiene e imposta i valori. Infine, il costruttore è definito con due parametri obbligatori:
[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
Questo attributo può essere applicato usando il nome completo, DeveloperAttribute
, oppure il nome abbreviato, Developer
, in uno dei modi seguenti:
[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)>
Il primo esempio mostra l'attributo applicato solo con i parametri denominati obbligatori. Il secondo esempio mostra l'attributo applicato con i parametri obbligatori e facoltativi.