Atributos no Visual Basic

A linguagem Visual Basic permite que o programador especifique modificadores em declarações, que representam informações sobre as entidades que estão sendo declaradas. Por exemplo, afixar um método de classe com os modificadoresPublic, Protectedou Protected FriendFriendPrivate especificar sua acessibilidade.

Além dos modificadores definidos pela linguagem, o Visual Basic também permite que os programadores criem novos modificadores, chamados de atributos, e os usem ao declarar novas entidades. Esses novos modificadores, que são definidos por meio da declaração de classes de atributo, são atribuídos a entidades por meio de blocos de atributo.

Observação. Os atributos podem ser recuperados em tempo de execução por meio das APIs de reflexão do .NET Framework. Essas APIs estão fora do escopo dessa especificação.

Por exemplo, uma estrutura pode definir um Help atributo que pode ser colocado em elementos de programa, como classes e métodos, para fornecer um mapeamento de elementos do programa para a documentação, como demonstra o exemplo a seguir:

<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

O exemplo define uma classe de atributo chamada HelpAttribute, ou Help , para abreviar, que tem um parâmetro posicional (UrlValue) e um argumento nomeado (Topic).

O exemplo a seguir mostra vários usos do atributo:

<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

O próximo exemplo verifica se Class1 tem um Help atributo e grava os valores associados Topic e Url se o atributo está 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

Classes de atributo

Uma classe de atributo é uma classe não genérica que deriva de System.Attribute e não MustInherité . A classe de atributo pode ter um System.AttributeUsage atributo que declara no que o atributo é válido, se ele pode ser usado várias vezes em uma declaração e se ele é herdado. O exemplo a seguir define uma classe de atributo nomeada SimpleAttribute que pode ser colocada em declarações de classe e declarações de interface:

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

O exemplo a seguir mostra alguns usos do Simple atributo. Embora a classe de atributo seja nomeada SimpleAttribute, os usos desse atributo podem omitir o Attribute sufixo, reduzindo assim o nome para Simple:

<Simple> Class Class1
End Class

<Simple> Interface Interface1
End Interface

Se o atributo não tiver um System.AttributeUsage, o atributo poderá ser colocado em qualquer destino (equivalente a AttributeTargets.All). O System.AttributeUsage atributo tem um inicializador de variável, AllowMultipleque especifica se o atributo indicado pode ser especificado mais de uma vez para uma determinada declaração. Se AllowMultiple for para um atributo True, será uma classe de atributo de uso múltiplo e poderá ser especificada mais de uma vez em uma declaração. Se AllowMultiple um atributo for ou não for False especificado para um atributo, ele será uma classe de atributo de uso único e poderá ser especificado no máximo uma vez em uma declaração.

O exemplo a seguir define uma classe de atributo de uso múltiplo chamada 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

O exemplo mostra uma declaração de classe com dois usos do Author atributo:

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

O System.AttributeUsage atributo tem uma variável de instância pública, Inheritedque especifica se o atributo, quando especificado em um tipo base, também é herdado por tipos que derivam desse tipo base. Se a Inherited variável de instância pública não for inicializada, um valor False padrão será usado. Propriedades e eventos não herdam atributos, embora os métodos definidos por propriedades e eventos o façam. As interfaces não herdam atributos.

Se um atributo de uso único for herdado e especificado em um tipo derivado, o atributo especificado no tipo derivado substituirá o atributo herdado. Se um atributo de uso múltiplo for herdado e especificado em um tipo derivado, ambos os atributos serão especificados no tipo derivado. Por exemplo:

<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

Os parâmetros posicionais do atributo são definidos pelos parâmetros dos construtores públicos da classe de atributo. Parâmetros posicionais devem ser ByVal e não podem especificar ByRef. As variáveis e as propriedades de instância pública são definidas por propriedades públicas de leitura/gravação ou variáveis de instância da classe de atributo. Os tipos que podem ser usados em parâmetros posicionais e variáveis de instância pública e propriedades são restritos a tipos de atributo. Um tipo será um tipo de atributo se for um dos seguintes:

  • Qualquer tipo primitivo, exceto e DateDecimal.

  • O tipo Object.

  • O tipo System.Type.

  • Um tipo enumerado, desde que ele e os tipos nos quais ele está aninhado (se houver) tenham Public acessibilidade.

  • Uma matriz unidimensional de um dos tipos anteriores nesta lista.

Blocos de atributo

Os atributos são especificados em blocos de atributo. Cada bloco de atributo é delimitado por colchetes angulares ("<>" e vários atributos podem ser especificados em uma lista separada por vírgulas dentro de um bloco de atributo ou em vários blocos de atributo. A ordem na qual os atributos são especificados não é significativa. Por exemplo, os blocos <A, B><B, A><A> <B> de atributo e <B> <A> todos são equivalentes.

Attributes
    : AttributeBlock+
    ;

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

AttributeList
    : Attribute ( Comma Attribute )*
    ;

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

AttributeModifier
    : 'Assembly' | 'Module'
    ;

Um atributo pode não ser especificado em um tipo de declaração que não dá suporte, e atributos de uso único podem não ser especificados mais de uma vez em um bloco de atributo. O exemplo a seguir causa erros porque ele tenta usar HelpString na interface Interface1 e mais de uma vez na declaração de 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

Um atributo consiste em um modificador de atributo opcional, um nome de atributo, uma lista opcional de argumentos posicionais e inicializadores de variável/propriedade. Se não houver parâmetros ou inicializadores, os parênteses poderão ser omitidos. Se um atributo tiver um modificador, ele deverá estar em um bloco de atributo na parte superior de um arquivo de origem.

Se um arquivo de origem contiver um bloco de atributo na parte superior do arquivo que especifica atributos para o assembly ou módulo que conterá o arquivo de origem, cada atributo no bloco de atributos deverá ser prefixado tanto pelo modificador quanto Module por Assembly dois-pontos.

Nomes de atributo

O nome de um atributo especifica uma classe de atributo. Por convenção, as classes de atributo são nomeadas com o sufixo Attribute. Os usos de um atributo podem incluir ou omitir esse sufixo. Consequentemente, o nome de uma classe de atributo que corresponde a um identificador de atributo é o próprio identificador ou a concatenação do identificador qualificado e Attribute. Quando o compilador resolve um nome de atributo, ele acrescenta Attribute ao nome e tenta a pesquisa. Se essa pesquisa falhar, o compilador tentará a pesquisa sem o sufixo. Por exemplo, os usos de uma classe SimpleAttribute de atributo podem omitir o Attribute sufixo, reduzindo assim o nome para Simple:

<Simple> Class Class1
End Class

<Simple> Interface Interface1
End Interface

O exemplo acima é semanticamente equivalente ao seguinte:

<SimpleAttribute> Class Class1
End Class

<SimpleAttribute> Interface Interface1
End Interface

Em geral, os atributos nomeados com o sufixo Attribute são preferenciais. O exemplo a seguir mostra duas classes de atributo nomeadas 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

O bloco <T> de atributo e o bloco <TAttribute> de atributo referem-se à classe de atributo chamada TAttribute. Não é possível usar T como um atributo até que você remova a declaração para a classe TAttribute.

Argumentos de atributo

Argumentos para um atributo podem ter duas formas: argumentos posicionais e inicializadores de variável/propriedade de instância. Todos os argumentos posicionais para o atributo devem preceder os inicializadores de variável/propriedade da instância. Um argumento posicional consiste em uma expressão constante, uma expressão de criação de matriz unidimensional ou uma GetType expressão. Um inicializador de variável/propriedade de instância consiste em um identificador, que pode corresponder a palavras-chave, seguido por dois-pontos e sinal de igual, e encerrado por uma expressão constante ou uma GetType expressão.

Considerando um atributo com classe Tde atributo, lista Pde argumentos posicionais e lista Nde inicializadores de variável/propriedade da instância, estas etapas determinam se os argumentos são válidos:

  1. Siga as etapas de processamento em tempo de compilação para compilar uma expressão do formulário New T(P). Isso resulta em um erro de tempo de compilação ou determina um construtor que T é mais aplicável à lista de argumentos.

  2. Se o construtor determinado na etapa 1 tiver parâmetros que não são tipos de atributo ou inacessíveis no site de declaração, ocorrerá um erro de tempo de compilação.

  3. Para cada inicializador Arg de variável/propriedade de instância, Nvamos Name ser o identificador da variável de instância/inicializador Argde propriedade. Name deve identificar uma propriedade nãoShared gravável, Public de instância ou sem parâmetros em T cujo tipo é um tipo de atributo. Se T não tiver essa variável de instância ou propriedade, ocorrerá um erro de tempo de compilação.

Por exemplo:

<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

Parâmetros de tipo não podem ser usados em qualquer lugar em argumentos de atributo. No entanto, os tipos construídos podem ser usados:

<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
    ;