Scrittura di attributi personalizzati
Aggiornamento: novembre 2007
Per progettare attributi personalizzati non è necessario apprendere numerosi nuovi concetti. Se si ha dimestichezza con la programmazione orientata ad oggetti e si è in grado di progettare classi si dispone già di gran parte delle conoscenze necessarie. Gli attributi personalizzati sono essenzialmente classi tradizionali derivate direttamente o indirettamente da System.Attribute. Analogamente alle classi tradizionali, gli attributi personalizzati contengono metodi per la memorizzazione e il recupero di dati.
Di seguito sono illustrati i passaggi iniziali di una corretta progettazione di classi di attributi personalizzati:
Applicazione di AttributeUsageAttribute
Dichiarazione della classe di un attributo
Dichiarazione dei costruttori
Dichiarazione delle proprietà
In questa sezione verrà descritto ciascuno di questi passaggi e verrà infine illustrato un esempio di attributo personalizzato.
Applicazione di AttributeUsageAttribute
La dichiarazione di un attributo personalizzato ha inizio con l'attributo 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 precisare gli elementi su cui l'attributo è applicabile. Nel frammento di codice riportato di seguito si illustra come utilizzare l'attributo AttributeUsageAttribute.
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
<AttributeUsage(AttributeTargets.All, Inherited := False, AllowMultiple := true)>
L'attributo System.AttributeUsageAttribute dispone di tre membri importanti per la creazione di attributi personalizzati: AttributeTargets, Inherited e AllowMultiple.
Membro AttributeTargets
Nell'esempio precedente è specificato AttributeTargets.All, per indicare che questo attributo è applicabile a tutti gli elementi di programma. In alternativa è possibile specificare AttributeTargets.Class oppure AttributeTargets.Method, a indicare che l'attributo può essere applicato rispettivamente solo a una classe o solo a un metodo. Tutti gli elementi di programma possono essere contrassegnati per essere descrivibili attraverso un attributo personalizzato.
È inoltre possibile passare più istanze di AttributeTargets. Nel frammento di codice riportato di seguito viene specificato che un attributo personalizzato può essere applicato a qualsiasi tipo o metodo.
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage (AttributeTargets.Class Or AttributeTargets.Method)>
Proprietà Inherited
La proprietà Inherited indica se l'attributo può essere ereditato da classi derivate dalle classi su cui l'attributo è applicato. Questa proprietà ammette i flag true e false. Il primo rappresenta il valore predefinito. Nell'esempio di codice riportato di seguito MyAttribute assume per Inherited il valore predefinito true, mentre YourAttribute assume per Inherited il valore 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
I due attributi vengono quindi applicati a un metodo della classe base 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
La classe YourClass viene infine ereditata dalla classe base MyClass. Il metodo MyMethod presenta MyAttribute, ma non 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
Proprietà AllowMultiple
La proprietà AllowMultiple indica se su un elemento possono esistere più istanze dell'attributo. Se è impostata su true saranno consentite più istanze, mentre se è impostata sul valore predefinito false potrà esistere un'unica istanza.
Nell'esempio di codice riportato di seguito MyAttribute assume per AllowMultiple il valore predefinito false, mentre YourAttribute assume il valore 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
Quando vengono applicate più istanze di questi attributi, viene generato un errore di compilazione per MyAttribute. Nell'esempio di codice riportato di seguito si illustra il corretto utilizzo di YourAttribute e l'utilizzo errato di 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
Se sia la proprietà AllowMultiple che la proprietà Inherited sono impostate su true una classe ereditata da un'altra classe potrà ereditare un attributo e nella stessa classe figlio potrà essere applicata una seconda istanza dello stesso attributo. Se AllowMultiple è impostata su false i valori di eventuali attributi della classe padre verranno sostituiti da quelli delle nuove istanze dello stesso attributo nella classe figlio.
Dichiarazione della classe di un attributo
Una volta applicato l'attributo AttributeUsageAttribute, è possibile iniziare a definire le specifiche dell'attributo stesso. La dichiarazione di una classe di attributi è analoga alla dichiarazione di una classe tradizionale, come illustrato nel seguente codice:
public class MyAttribute : System.Attribute
{
// . . .
}
' This attribute is only usable with methods
<AttributeUsage(AttributeTargets.Method)> Public Class MyAttribute
Inherits System.Attribute
' . . .
End Class
Questa definizione di attributo dimostra i seguenti punti:
Le classi di attributo devono essere dichiarate come classi pubbliche.
Per convenzione, il nome della classe di attributo termina con la parola Attribute. Benché non sia obbligatorio, il rispetto di tale convenzione migliora la leggibilità del codice. Quando si applica l'attributo, l'inclusione della parola Attribute è facoltativa.
Tutte le classi di attributi devono ereditare direttamente o indirettamente da System.Attribute.
In Microsoft Visual Basic tutte le classi di attributi personalizzati devono presentare l'attributo AttributeUsageAttribute.
Dichiarazione dei costruttori
Gli attributi vengono inizializzati con i costruttori con modalità analoghe a quelle delle classi tradizionali. Nel frammento di codice riportato di seguito viene illustrato un tipico costruttore di attributi. Questo costruttore pubblico riceve un parametro e ne assegna il valore a una variabile membro.
public MyAttribute(bool myvalue)
{
this.myvalue = myvalue;
}
Public Sub New(newvalue As Boolean)
Me.myvalue = newvalue
End Sub
È possibile sovraccaricare il costruttore per inserire diverse combinazioni di valori. Se si definisce anche una proprietà per la classe di attributi personalizzati sarà possibile utilizzare una combinazione di parametri predefiniti e posizionali all'inizializzazione dell'attributo. Tutti i parametri obbligatori vengono in genere definiti come parametri posizionali e tutti i parametri facoltativi come parametri predefiniti. In questo caso non è possibile inizializzare l'attributo senza il parametro obbligatorio. Tutti gli altri parametri sono facoltativi. In Visual Basic l'argomento ParamArray non deve essere utilizzato con i costruttori di una classe di attributi.
Nell'esempio di codice riportato di seguito si illustra come applicare un attributo che si avvale del costruttore precedente mediante parametri facoltativi e obbligatori. Nell'esempio si presuppone che l'attributo presenti un valore Boolean obbligatorio e una proprietà della stringa facoltativa.
//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)>
Dichiarazione delle proprietà
Se si desidera definire un parametro predefinito o fornire un metodo semplice per la restituzione dei valori memorizzati dall'attributo dichiarare una proprietà. Le proprietà di un 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. Nell'esempio di codice che segue si illustra come implementare una proprietà semplice nell'attributo.
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
In questa sezione, basata sulle informazioni descritte in precedenza, si illustra come progettare un attributo semplice che offra informazioni sull'autore di una sezione del codice. Nell'attributo di questo esempio è memorizzato il nome e il livello del programmatore ed è specificato se il codice è stato rivisto. Per la memorizzazione dei valori effettivi da salvare vengono utilizzate tre variabili private. Ciascuna variabile è rappresentata da una proprietà pubblica che consente di visualizzare e impostare i valori. Il costruttore viene infine definito con due parametri obbligatori.
[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
È possibile applicare questo attributo utilizzandone il nome completo, DeveloperAttribute, oppure il nome abbreviato, Developer, in uno dei modi descritti di seguito.
[Developer("Joan Smith", "1")]
[Developer("Joan Smith", "1", Reviewed = true)]
<Developer("Joan Smith", "1")>
<Developer("Joan Smith", "1", Reviewed := True)>
Nel primo esempio è mostrata l'applicazione dell'attributo con i soli parametri predefiniti obbligatori, mentre nel secondo esempio è illustrata l'applicazione dell'attributo sia con i parametri obbligatori che con quelli facoltativi.