Attributi in Visual Basic

Il linguaggio Visual Basic consente al programmatore di specificare modificatori per le dichiarazioni, che rappresentano informazioni sulle entità dichiarate. Ad esempio, l'associazione di un metodo di classe con i modificatori Public, , Protected FriendProtectedFriend, o Private ne specifica l'accessibilità.

Oltre ai modificatori definiti dal linguaggio, Visual Basic consente anche ai programmatori di creare nuovi modificatori, denominati attributi e di usarli per dichiarare nuove entità. Questi nuovi modificatori, definiti tramite la dichiarazione delle classi di attributi, vengono quindi assegnati alle entità tramite blocchi di attributi.

Nota. Gli attributi possono essere recuperati in fase di esecuzione tramite le API di reflection di .NET Framework. Queste API non rientrano nell'ambito di questa specifica.

Ad esempio, un framework potrebbe definire un Help attributo che può essere inserito su elementi del programma, ad esempio classi e metodi per fornire un mapping dagli elementi del programma alla documentazione, come illustrato nell'esempio seguente:

<AttributeUsage(AttributeTargets.All)> _
Public Class HelpAttribute
    Inherits Attribute

    Public Sub New(urlValue As String)
        Me.UrlValue = urlValue
    End Sub

    Public Topic As String
    Private UrlValue As String

    Public ReadOnly Property Url() As String
        Get
            Return UrlValue
        End Get
    End Property
End Class

L'esempio definisce una classe di attributi denominata HelpAttribute, o Help per brevità, con un parametro posizionale (UrlValue) e un argomento denominato (Topic).

L'esempio seguente mostra diversi usi dell'attributo :

<Help("http://www.example.com/.../Class1.htm")> _
Public Class Class1
    <Help("http://www.example.com/.../Class1.htm", Topic:="F")> _
    Public Sub F()
    End Sub
End Class

Nell'esempio seguente viene verificato se Class1 ha un Help attributo e vengono scritti i valori e Url associati Topic se l'attributo è presente.

Module Test
    Sub Main()
        Dim type As Type = GetType(Class1)
        Dim arr() As Object = _
            type.GetCustomAttributes(GetType(HelpAttribute), True)

        If arr.Length = 0 Then
            Console.WriteLine("Class1 has no Help attribute.")
        Else
            Dim ha As HelpAttribute = CType(arr(0), HelpAttribute)
            Console.WriteLine("Url = " & ha.Url & ", Topic = " & ha.Topic)
        End If
    End Sub
End Module

Classi di attributi

Una classe di attributi è una classe non generica che deriva da System.Attribute e non MustInheritè . La classe di attributi può avere un System.AttributeUsage attributo che dichiara l'attributo su cui è valido, se può essere usato più volte in una dichiarazione e se viene ereditato. L'esempio seguente definisce una classe di attributi denominata che può essere inserita SimpleAttribute nelle dichiarazioni di classe e nelle dichiarazioni di interfaccia:

<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Interface)> _
Public Class SimpleAttribute
    Inherits System.Attribute
End Class

Nell'esempio seguente vengono illustrati alcuni usi dell'attributo Simple . Anche se la classe dell'attributo è denominata SimpleAttribute, gli usi di questo attributo possono omettere il Attribute suffisso, riducendo così il nome in Simple:

<Simple> Class Class1
End Class

<Simple> Interface Interface1
End Interface

Se l'attributo non dispone di un , System.AttributeUsagel'attributo può essere inserito in qualsiasi destinazione (equivalente a AttributeTargets.All). L'attributo System.AttributeUsage ha un inizializzatore variabile, AllowMultiple, che specifica se l'attributo indicato può essere specificato più volte per una determinata dichiarazione. Se AllowMultiple per un attributo è True, è una classe di attributi a uso multiplo e può essere specificata più volte in una dichiarazione. Se AllowMultiple per un attributo è o non specificato False per un attributo, è una classe di attributi a uso singolo e può essere specificata al massimo una volta in una dichiarazione.

L'esempio seguente definisce una classe di attributi a più usi denominata AuthorAttribute:

<AttributeUsage(AttributeTargets.Class, AllowMultiple:=True)> _
Public Class AuthorAttribute
    Inherits System.Attribute

    Private _Value As String

    Public Sub New(value As String)
        Me._Value = value
    End Sub

    Public ReadOnly Property Value() As String
        Get
            Return _Value
        End Get
    End Property
End Class

L'esempio mostra una dichiarazione di classe con due usi dell'attributo Author :

<Author("Maria Hammond"), Author("Ramesh Meyyappan")> _
Class Class1
End Class

L'attributo System.AttributeUsage ha una variabile di istanza pubblica, Inherited, che specifica se l'attributo, se specificato in un tipo di base, viene ereditato anche dai tipi che derivano da questo tipo di base. Se la Inherited variabile di istanza pubblica non viene inizializzata, viene usato un valore predefinito di False . Le proprietà e gli eventi non ereditano attributi, anche se i metodi definiti dalle proprietà e dagli eventi lo fanno. Le interfacce non ereditano attributi.

Se un attributo a utilizzo singolo viene ereditato e specificato in un tipo derivato, l'attributo specificato nel tipo derivato esegue l'override dell'attributo ereditato. Se un attributo a uso multiplo è ereditato e specificato in un tipo derivato, entrambi gli attributi vengono specificati nel tipo derivato. Per esempio:

<AttributeUsage(AttributeTargets.Class, AllowMultiple:=True, _
                Inherited:=True) > _
Class MultiUseAttribute
    Inherits System.Attribute

    Public Sub New(value As Boolean)
    End Sub
End Class

<AttributeUsage(AttributeTargets.Class, Inherited:=True)> _
Class SingleUseAttribute
    Inherits Attribute

    Public Sub New(value As Boolean)
    End Sub
End Class

<SingleUse(True), MultiUse(True)> Class Base
End Class

' Derived has three attributes defined on it: SingleUse(False),
' MultiUse(True) and MultiUse(False)
<SingleUse(False), MultiUse(False)> _
Class Derived
    Inherits Base
End Class

I parametri posizionali dell'attributo sono definiti dai parametri dei costruttori pubblici della classe di attributi. I parametri posizionali devono essere ByVal e potrebbero non specificare ByRef. Le variabili e le proprietà dell'istanza pubblica sono definite dalle proprietà pubbliche di lettura/scrittura o dalle variabili di istanza della classe dell'attributo. I tipi che possono essere usati nei parametri posizionali e nelle variabili e nelle proprietà dell'istanza pubblica sono limitati ai tipi di attributo. Un tipo è un tipo di attributo se è uno dei seguenti:

  • Qualsiasi tipo primitivo ad eccezione di Date e Decimal.

  • Tipo Object.

  • Tipo System.Type.

  • Tipo enumerato, a condizione che e i tipi in cui è annidato (se presente) abbiano Public accessibilità.

  • Matrice unidimensionale di uno dei tipi precedenti in questo elenco.

Blocchi di attributi

Gli attributi vengono specificati nei blocchi di attributi. Ogni blocco di attributi è delimitato da parentesi angolari ("<>") e più attributi possono essere specificati in un elenco delimitato da virgole all'interno di un blocco di attributi o in più blocchi di attributi. L'ordine in cui vengono specificati gli attributi non è significativo. Ad esempio, l'attributo blocca <A, B>, <A> <B><B, A>e <B> <A> sono tutti equivalenti.

Attributes
    : AttributeBlock+
    ;

AttributeBlock
    : LineTerminator? '<' AttributeList LineTerminator? '>' LineTerminator?
    ;

AttributeList
    : Attribute ( Comma Attribute )*
    ;

Attribute
    : ( AttributeModifier ':' )? SimpleTypeName
    ( OpenParenthesis AttributeArguments? CloseParenthesis )?
    ;

AttributeModifier
    : 'Assembly' | 'Module'
    ;

Un attributo non può essere specificato in un tipo di dichiarazione che non supporta e gli attributi a utilizzo singolo potrebbero non essere specificati più volte in un blocco di attributi. L'esempio seguente causa errori sia perché tenta di usare HelpString nell'interfaccia Interface1 e più volte nella dichiarazione di Class1.

<AttributeUsage(AttributeTargets.Class)> _
Public Class HelpStringAttribute
    Inherits System.Attribute

    Private InternalValue As String

    Public Sub New(value As String)
        Me.InternalValue = value
    End Sub

    Public ReadOnly Property Value() As String
        Get
            Return InternalValue
        End Get
    End Property
End Class

' Error: HelpString only applies to classes.
<HelpString("Description of Interface1")> _
Interface Interface1
    Sub Sub1()
End Interface

' Error: HelpString is single-use.
<HelpString("Description of Class1"), _
    HelpString("Another description of Class1")> _
Public Class Class1
End Class

Un attributo è costituito da un modificatore di attributi facoltativo, un nome di attributo, un elenco facoltativo di argomenti posizionali e inizializzatori di variabile/proprietà. Se non sono presenti parametri o inizializzatori, è possibile omettere le parentesi. Se un attributo ha un modificatore, deve trovarsi in un blocco di attributi nella parte superiore di un file di origine.

Se un file di origine contiene un blocco di attributi nella parte superiore del file che specifica gli attributi per l'assembly o il modulo che conterrà il file di origine, ogni attributo nel blocco di attributi deve essere preceduto sia dal modificatore o Module che da Assembly due punti.

Nomi degli attributi

Il nome di un attributo specifica una classe di attributi. Per convenzione, le classi di attributi vengono denominate con il suffisso Attribute. Gli usi di un attributo possono includere o omettere questo suffisso. Di conseguenza, il nome di una classe di attributi che corrisponde a un identificatore di attributo è l'identificatore stesso o la concatenazione dell'identificatore completo e Attribute. Quando il compilatore risolve un nome di attributo, aggiunge Attribute al nome e prova la ricerca. Se la ricerca ha esito negativo, il compilatore prova la ricerca senza il suffisso . Ad esempio, gli usi di una classe SimpleAttribute di attributi possono omettere il Attribute suffisso, riducendo così il nome in Simple:

<Simple> Class Class1
End Class

<Simple> Interface Interface1
End Interface

L'esempio precedente è semanticamente equivalente al seguente:

<SimpleAttribute> Class Class1
End Class

<SimpleAttribute> Interface Interface1
End Interface

In generale, gli attributi denominati con il suffisso Attribute sono preferiti. Nell'esempio seguente vengono illustrate due classi di attributi denominate T e T``Attribute.

<AttributeUsage(AttributeTargets.All)> _
Public Class T
    Inherits System.Attribute
End Class

<AttributeUsage(AttributeTargets.All)> _
Public Class TAttribute
    Inherits System.Attribute
End Class

' Refers to TAttribute.
<T> Class Class1 
End Class

' Refers to TAttribute.
<TAttribute> Class Class2 
End Class

Sia il blocco di attributi che il blocco <T><TAttribute> di attributi fanno riferimento alla classe di attributi denominata TAttribute. Non è possibile usare T come attributo finché non si rimuove la dichiarazione per la classe TAttribute.

Argomenti attributo

Gli argomenti di un attributo possono assumere due forme: argomenti posizionali e inizializzatori variabile/proprietà di istanza. Tutti gli argomenti posizionali dell'attributo devono precedere gli inizializzatori di variabile/proprietà di istanza. Un argomento posizionale è costituito da un'espressione costante, un'espressione di creazione di matrice unidimensionale o un'espressione GetType . Un inizializzatore di variabile/proprietà di istanza è costituito da un identificatore, che può corrispondere a parole chiave, seguito da due punti e segno di uguale e terminato da un'espressione costante o da un'espressione GetType .

Dato un attributo con la classe Tdi attributi , l'elenco di argomenti posizionali e l'elenco NPdi inizializzatori variabile/proprietà dell'istanza , questi passaggi determinano se gli argomenti sono validi:

  1. Seguire i passaggi di elaborazione in fase di compilazione per compilare un'espressione del modulo New T(P). Ciò comporta un errore in fase di compilazione o determina un costruttore in T che è più applicabile all'elenco di argomenti.

  2. Se il costruttore determinato nel passaggio 1 contiene parametri che non sono tipi di attributo o non sono accessibili nel sito di dichiarazione, si verifica un errore in fase di compilazione.

  3. Per ogni inizializzatore Arg di variabile/proprietà di istanza in N, lasciare Name che sia l'identificatore dell'inizializzatore Argdi variabile/proprietà dell'istanza . Name deve identificare una proprietà nonShared scrivibile, Public variabile di istanza o senza parametri nel T cui tipo è un tipo di attributo. Se T non ha una variabile o una proprietà di istanza di questo tipo, si verifica un errore in fase di compilazione.

Per esempio:

<AttributeUsage(AttributeTargets.All)> _
Public Class GeneralAttribute
    Inherits Attribute

    Public Sub New(x As Integer)
    End Sub

    Public Sub New(x As Double)
    End Sub

    Public y As Type

    Public Property z As Integer
        Get
        End Get

        Set
        End Set
    End Property
End Class

' Calls the first constructor.
<General(10, z:=30, y:=GetType(Integer))> _
Class C1
End Class

' Calls the second constructor.
<General(10.5, z:=10)> _
Class C2
End Class

I parametri di tipo non possono essere usati in qualsiasi posizione negli argomenti dell'attributo. Tuttavia, è possibile usare i tipi costruiti:

<AttributeUsage(AttributeTargets.All)> _
Class A 
   Inherits System.Attribute 

   Public Sub New(t As Type)
   End Sub 
End Class

Class List(Of T) 
    ' Error: attribute argument cannot use type parameter
    <A(GetType(T))> Dim t1 As T 

    ' OK: closed type
    <A(GetType(List(Of Integer)))> Dim y As Integer
End Class
AttributeArguments
    : AttributePositionalArgumentList
    | AttributePositionalArgumentList Comma VariablePropertyInitializerList
    | VariablePropertyInitializerList
    ;

AttributePositionalArgumentList
    : AttributeArgumentExpression? ( Comma AttributeArgumentExpression? )*
    ;

VariablePropertyInitializerList
    : VariablePropertyInitializer ( Comma VariablePropertyInitializer )*
    ;

VariablePropertyInitializer
    : IdentifierOrKeyword ColonEquals AttributeArgumentExpression
    ;

AttributeArgumentExpression
    : ConstantExpression
    | GetTypeExpression
    | ArrayExpression
    ;