Atrybuty w Visual Basic

Język Visual Basic umożliwia programistom określanie modyfikatorów deklaracji, które reprezentują informacje o zadeklarowanych jednostkach. Na przykład umieszczenie metody klasy z modyfikatorami Public, Protected, Friend, Protected Friendlub Private określa jej dostępność.

Oprócz modyfikatorów zdefiniowanych przez język język Visual Basic umożliwia również programistom tworzenie nowych modyfikatorów, nazywanych atrybutami i używanie ich podczas deklarowania nowych jednostek. Te nowe modyfikatory zdefiniowane za pomocą deklaracji klas atrybutów są następnie przypisywane do jednostek za pomocą bloków atrybutów.

Uwaga. Atrybuty mogą być pobierane w czasie wykonywania za pośrednictwem interfejsów API odbicia programu .NET Framework. Te interfejsy API wykraczają poza zakres tej specyfikacji.

Na przykład struktura może definiować Help atrybut, który można umieścić w elementach programu, takich jak klasy i metody, aby zapewnić mapowanie z elementów programu do dokumentacji, jak pokazano w poniższym przykładzie:

<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

W przykładzie zdefiniowano klasę atrybutów o nazwie HelpAttributelub Help na krótko, która ma jeden parametr pozycyjny (UrlValue) i jeden nazwany argument (Topic).

W następnym przykładzie pokazano kilka zastosowań atrybutu:

<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

Następny przykład sprawdza, czy Class1 ma Help atrybut, i zapisuje skojarzone wartości Topic i Url jeśli atrybut jest obecny.

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

Klasy atrybutów

Klasa atrybutów jest klasą niegeneryjną, która pochodzi z System.Attribute klasy i nie MustInheritjest . Klasa atrybutów może mieć System.AttributeUsage atrybut, który deklaruje, co atrybut jest prawidłowy, czy może być używany wiele razy w deklaracji i czy jest dziedziczony. W poniższym przykładzie zdefiniowano klasę atrybutów o nazwie SimpleAttribute , którą można umieścić w deklaracjach klas i deklaracjach interfejsu:

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

W następnym przykładzie pokazano kilka zastosowań atrybutu Simple . Mimo że klasa atrybutów ma nazwę SimpleAttribute, użycie tego atrybutu może pominąć Attribute sufiks, skracając w ten sposób nazwę na Simple:

<Simple> Class Class1
End Class

<Simple> Interface Interface1
End Interface

Jeśli atrybut nie ma atrybutu System.AttributeUsage, atrybut można umieścić na dowolnym obiekcie docelowym (odpowiednik AttributeTargets.All). Atrybut System.AttributeUsage ma inicjator zmiennej , który określa, AllowMultipleczy wskazany atrybut można określić więcej niż raz dla danej deklaracji. Jeśli AllowMultiple dla atrybutu jest True, jest to klasa atrybutów wielokrotnego użycia i może być określona więcej niż raz w deklaracji. Jeśli AllowMultiple atrybut jest False lub nieokreślony dla atrybutu, jest to klasa atrybutów pojedynczego użycia i może być określona co najwyżej raz w deklaracji.

W poniższym przykładzie zdefiniowano klasę atrybutów wielokrotnego użycia o nazwie 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

W przykładzie pokazano deklarację klasy z dwoma zastosowaniami atrybutu Author :

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

Atrybut System.AttributeUsage ma publiczną zmienną wystąpienia , Inheritedktóra określa, czy atrybut, po określeniu w typie podstawowym, jest również dziedziczony przez typy pochodzące z tego typu podstawowego. Jeśli publiczna zmienna Inherited wystąpienia nie jest inicjowana, zostanie użyta wartość domyślna False . Właściwości i zdarzenia nie dziedziczą atrybutów, chociaż metody zdefiniowane przez właściwości i zdarzenia działają. Interfejsy nie dziedziczą atrybutów.

Jeśli atrybut pojedynczego użycia jest dziedziczony i określony w typie pochodnym, atrybut określony w typie pochodnym zastępuje dziedziczony atrybut. Jeśli atrybut wielokrotnego użycia jest dziedziczony i określony w typie pochodnym, oba atrybuty są określone w typie pochodnym. Przykład:

<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

Parametry pozycyjne atrybutu są definiowane przez parametry publicznych konstruktorów klasy atrybutów. Parametry pozycyjne muszą być ByVal i mogą nie określać ByRef. Zmienne i właściwości wystąpienia publicznego są definiowane przez publiczne właściwości odczytu i zapisu lub zmienne wystąpienia klasy atrybutu. Typy, które mogą być używane w parametrach pozycyjnych i publicznych zmiennych wystąpień i właściwości, są ograniczone do typów atrybutów. Typ jest typem atrybutu, jeśli jest to jeden z następujących elementów:

  • Dowolny typ pierwotny z wyjątkiem i DateDecimal.

  • Typ Object.

  • Typ System.Type.

  • Typ wyliczony, pod warunkiem, że jest on i typy, w których jest zagnieżdżona (jeśli istnieje) mają Public ułatwienia dostępu.

  • Jednowymiarowa tablica jednego z poprzednich typów na tej liście.

Bloki atrybutów

Atrybuty są określane w blokach atrybutów. Każdy blok atrybutów jest rozdzielany nawiasami kątowymi ("<>"), a wiele atrybutów można określić na liście rozdzielanej przecinkami w bloku atrybutu lub w wielu blokach atrybutów. Kolejność, w której określono atrybuty, nie jest znacząca. Na przykład atrybut bloki <A, B>, <B, A><A> <B> i <B> <A> są równoważne.

Attributes
    : AttributeBlock+
    ;

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

AttributeList
    : Attribute ( Comma Attribute )*
    ;

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

AttributeModifier
    : 'Assembly' | 'Module'
    ;

Atrybut może nie być określony w rodzaju deklaracji, który nie obsługuje, a atrybuty pojedynczego użycia mogą nie być określone więcej niż raz w bloku atrybutów. Poniższy przykład powoduje błędy zarówno dlatego, że próbuje użyć HelpString w interfejsie Interface1 , jak i więcej niż raz w deklaracji 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

Atrybut składa się z opcjonalnego modyfikatora atrybutów, nazwy atrybutu, opcjonalnej listy argumentów pozycyjnych i inicjatorów zmiennych/właściwości. Jeśli nie ma parametrów ani inicjatorów, nawiasy mogą zostać pominięte. Jeśli atrybut ma modyfikator, musi znajdować się w bloku atrybutów w górnej części pliku źródłowego.

Jeśli plik źródłowy zawiera blok atrybutów w górnej części pliku, który określa atrybuty zestawu lub modułu, który będzie zawierać plik źródłowy, każdy atrybut w bloku atrybutów musi być poprzedzony zarówno Assembly modyfikatorem, Module jak i dwukropkiem.

Nazwy atrybutów

Nazwa atrybutu określa klasę atrybutów. Zgodnie z konwencją klasy atrybutów mają nazwę z sufiksem Attribute. Użycie atrybutu może zawierać lub pomijać ten sufiks. W związku z tym nazwa klasy atrybutu, która odpowiada identyfikatorowi atrybutu, jest samym identyfikatorem lub łączeniem kwalifikowanego identyfikatora i Attribute. Gdy kompilator rozpozna nazwę atrybutu, dołącza Attribute ją do nazwy i próbuje wyszukać. Jeśli wyszukiwanie zakończy się niepowodzeniem, kompilator spróbuje wyszukać bez sufiksu. Na przykład użycie klasy SimpleAttribute atrybutów może pominąć Attribute sufiks, skracając w ten sposób nazwę na Simple:

<Simple> Class Class1
End Class

<Simple> Interface Interface1
End Interface

Powyższy przykład jest semantycznie równoważny z następującymi elementami:

<SimpleAttribute> Class Class1
End Class

<SimpleAttribute> Interface Interface1
End Interface

Ogólnie rzecz biorąc, preferowane są atrybuty o nazwie z sufiksem Attribute . W poniższym przykładzie przedstawiono dwie klasy atrybutów o nazwach T i 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

Blok atrybutów <T> i blok <TAttribute> atrybutów odwołują się do klasy atrybutów o nazwie TAttribute. Nie można użyć T jako atrybutu, dopóki nie usuniesz deklaracji klasy TAttribute.

Argumenty atrybutów

Argumenty atrybutu mogą mieć dwie formy: argumenty pozycyjne i inicjatory zmiennej wystąpienia/właściwości. Wszelkie argumenty pozycyjne atrybutu muszą poprzedzać inicjatory zmiennej wystąpienia/właściwości. Argument pozycyjny składa się z wyrażenia stałego, jednowymiarowego wyrażenia tworzenia tablicy lub GetType wyrażenia. Inicjator zmiennej wystąpienia/właściwości składa się z identyfikatora, który może być zgodny ze słowami kluczowymi, a następnie dwukropkiem i znakiem GetType równości, a następnie zakończonym przez wyrażenie stałe lub wyrażenie.

Biorąc pod uwagę atrybut z klasą Tatrybutów , listą Pargumentów pozycyjnych i listą Ninicjatora zmiennej wystąpienia/właściwości , te kroki określają, czy argumenty są prawidłowe:

  1. Wykonaj kroki przetwarzania w czasie kompilacji, aby skompilować wyrażenie formularza New T(P). Powoduje to błąd w czasie kompilacji lub określa konstruktor, T który ma największe zastosowanie do listy argumentów.

  2. Jeśli konstruktor określony w kroku 1 ma parametry, które nie są typami atrybutów lub są niedostępne w lokacji deklaracji, wystąpi błąd czasu kompilacji.

  3. Dla każdego inicjatora Arg zmiennej wystąpienia/właściwości w Nprogramie należy posłużyć Name się identyfikatorem inicjatora Argzmiennej wystąpienia/właściwości . Name musi identyfikowaćShared właściwość nie-, zapisywalną, Public zmienną wystąpienia lub bez parametrów, na T której typ jest typem atrybutu. Jeśli T nie ma takiej zmiennej lub właściwości wystąpienia, wystąpi błąd czasu kompilacji.

Przykład:

<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

Parametrów typu nie można używać nigdzie w argumentach atrybutów. Jednak mogą być używane typy skonstruowane:

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