The Visual Basic language enables the programmer to specify modifiers on declarations, which represent information about the entities being declared. For example, affixing a class method with the modifiers Public
, Protected
, Friend
, Protected Friend
, or Private
specifies its accessibility.
In addition to the modifiers defined by the language, Visual Basic also enables programmers to create new modifiers, called attributes, and to use them when declaring new entities. These new modifiers, which are defined through the declaration of attribute classes, are then assigned to entities through attribute blocks.
Note. Attributes may be retrieved at run time through the .NET Framework's reflection APIs. These APIs are outside the scope of this specification.
For instance, a framework might define a Help
attribute that can be placed on program elements such as classes and methods to provide a mapping from program elements to documentation, as the following example demonstrates:
<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
The example defines an attribute class named HelpAttribute
, or Help
for short, that has one positional parameter (UrlValue
) and one named argument (Topic
).
The next example shows several uses of the attribute:
<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
The next example checks to see if Class1
has a Help
attribute, and writes out the associated Topic
and Url
values if the attribute is present.
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
An attribute class is a non-generic class that derives from System.Attribute
and is not MustInherit
. The attribute class may have a System.AttributeUsage
attribute that declares what the attribute is valid on, whether it may be used multiple times in a declaration, and whether it is inherited. The following example defines an attribute class named SimpleAttribute
that can be placed on class declarations and interface declarations:
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Interface)> _
Public Class SimpleAttribute
Inherits System.Attribute
End Class
The next example shows a few uses of the Simple
attribute. Although the attribute class is named SimpleAttribute
, uses of this attribute may omit the Attribute
suffix, thus shortening the name to Simple
:
<Simple> Class Class1
End Class
<Simple> Interface Interface1
End Interface
If the attribute lacks a System.AttributeUsage
, then the attribute can be placed on any target (equivalent to AttributeTargets.All
). The System.AttributeUsage
attribute has a variable initializer, AllowMultiple
, which specifies whether the indicated attribute can be specified more than once for a given declaration. If AllowMultiple
for an attribute is True
, it is a multiple-use attribute class, and can be specified more than once on a declaration. If AllowMultiple
for an attribute is False
or unspecified for an attribute, it is a single-use attribute class, and can be specified at most once on a declaration.
The following example defines a multiple-use attribute class named 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
The example shows a class declaration with two uses of the Author
attribute:
<Author("Maria Hammond"), Author("Ramesh Meyyappan")> _
Class Class1
End Class
The System.AttributeUsage
attribute has a public instance variable, Inherited
, that specifies whether the attribute, when specified on a base type, is also inherited by types that derive from this base type. If the Inherited
public instance variable is not initialized, a default value of False
is used. Properties and events do not inherit attributes, although the methods defined by properties and events do. Interfaces do not inherit attributes.
If a single-use attribute is both inherited and specified on a derived type, the attribute specified on the derived type overrides the inherited attribute. If a multiple-use attribute is both inherited and specified on a derived type, both attributes are specified on the derived type. For example:
<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
The positional parameters of the attribute are defined by the parameters of the public constructors of the attribute class. Positional parameters must be ByVal
and may not specify ByRef
. Public instance variables and properties are defined by public read-write properties or instance variables of the attribute class. The types that can be used in positional parameters and public instance variables and properties are restricted to attribute types. A type is an attribute type if it is one of the following:
-
Any primitive type except for
Date
andDecimal
. -
The type
Object
. -
The type
System.Type
. -
An enumerated type, provided that it and the types in which it is nested (if any) have
Public
accessibility. -
A one-dimensional array of one of the previous types in this list.
Attributes are specified in attribute blocks. Each attribute block is delimited by angle brackets ("<>"), and multiple attributes can be specified in a comma-separated list within an attribute block or in multiple attribute blocks. The order in which attributes are specified is not significant. For example, the attribute blocks <A, B>
, <B, A>
, <A> <B>
and <B> <A>
are all equivalent.
Attributes
: AttributeBlock+
;
AttributeBlock
: LineTerminator? '<' AttributeList LineTerminator? '>' LineTerminator?
;
AttributeList
: Attribute ( Comma Attribute )*
;
Attribute
: ( AttributeModifier ':' )? SimpleTypeName
( OpenParenthesis AttributeArguments? CloseParenthesis )?
;
AttributeModifier
: 'Assembly' | 'Module'
;
An attribute may not be specified on a kind of declaration it does not support, and single-use attributes may not be specified more than once in an attribute block. The example below causes errors both because it attempts to use HelpString
on the interface Interface1
and more than once on the declaration of 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
An attribute consists of an optional attribute modifier, an attribute name, an optional list of positional arguments, and variable/property initializers. If there are no parameters or initializers, the parentheses may be omitted. If an attribute has a modifier, it must be in an attribute block at the top of a source file.
If a source file contains an attribute block at the top of the file that specifies attributes for the assembly or module that will contain the source file, each attribute in the attribute block must be prefixed by both the Assembly
or Module
modifier and a colon.
The name of an attribute specifies an attribute class. By convention, attribute classes are named with the suffix Attribute
. Uses of an attribute may either include or omit this suffix. Consequently the name of an attribute class that corresponds to an attribute identifier is either the identifier itself or the concatenation of the qualified identifier and Attribute
. When the compiler resolves an attribute name, it appends Attribute
to the name and tries the lookup. If that lookup fails, the compiler tries the lookup without the suffix. For example, uses of an attribute class SimpleAttribute
may omit the Attribute
suffix, thus shortening the name to Simple
:
<Simple> Class Class1
End Class
<Simple> Interface Interface1
End Interface
The example above is semantically equivalent to the following:
<SimpleAttribute> Class Class1
End Class
<SimpleAttribute> Interface Interface1
End Interface
In general, attributes named with the suffix Attribute
are preferred. The following example shows two attribute classes named T
and 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
Both the attribute block <T>
and the attribute block <TAttribute>
refer to the attribute class named TAttribute
. It is not possible to use T
as an attribute until you remove the declaration for class TAttribute
.
Arguments to an attribute may take two forms: positional arguments and instance variable/property initializers. Any positional arguments to the attribute must precede the instance variable/property initializers. A positional argument consists of a constant expression, a one-dimensional array-creation expression or a GetType
expression. An instance variable/property initializer consists of an identifier, which can match keywords, followed by a colon and equal sign, and terminated by a constant expression or a GetType
expression.
Given an attribute with attribute class T
, positional argument list P
, and instance variable/property initializer list N
, these steps determine whether the arguments are valid:
-
Follow the compile-time processing steps for compiling an expression of the form
New T(P)
. This either results in a compile-time error or determines a constructor onT
that is most applicable to the argument list. -
If the constructor determined in step 1 has parameters that are not attribute types or is inaccessible at the declaration site, a compile-time error occurs.
-
For each instance variable/property initializer
Arg
inN
, letName
be the identifier of the instance variable/property initializerArg
.Name
must identify a non-Shared
, writeable,Public
instance variable or parameterless property onT
whose type is an attribute type. IfT
has no such instance variable or property, a compile-time error occurs.
For example:
<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
Type parameters cannot be used anywhere in attribute arguments. However, constructed types may be used:
<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
;