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.

Vedi anche