Visual Basic 语言使程序员能够指定声明的修饰符,这些修饰符表示有关要声明的实体的信息。 例如,将类方法与修饰符 PublicProtected、、Friend或Protected FriendPrivate指定其辅助功能相附加。
除了语言定义的修饰符之外,Visual Basic 还使程序员能够创建新的修饰符,称为 属性,并在声明新实体时使用这些修饰符。 然后,通过属性类声明定义的这些新修饰符通过 属性块分配给实体。
注意。 可以通过 .NET Framework 的反射 API 在运行时检索属性。 这些 API 超出了此规范的范围。
例如,框架可以定义 Help 可放置在程序元素(如类和方法)上的属性,以提供从程序元素到文档的映射,如以下示例所示:
<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
该示例定义一个名为 HelpAttribute(或 Help 短)的属性类,该类具有一个位置参数(UrlValue)和一个命名参数(Topic)。
下一个示例演示属性的多个用法:
<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
下一个示例检查是否有 Class1 属性 Help ,并写出关联 Topic 属性和 Url 值(如果存在)。
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
属性类
特性类是派生自System.Attribute非泛型类且不是MustInherit泛型类。 特性类可能具有一个 System.AttributeUsage 属性,该属性声明该特性是否有效、是否可以在声明中多次使用,以及属性是否继承。 以下示例定义一个名为的属性类,该类 SimpleAttribute 可放置在类声明和接口声明上:
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Interface)> _
Public Class SimpleAttribute
Inherits System.Attribute
End Class
下一个示例演示了 Simple 该属性的一些用法。 虽然特性类已命名 SimpleAttribute,但此属性的使用可能会省略 Attribute 后缀,从而将名称缩短为 Simple:
<Simple> Class Class1
End Class
<Simple> Interface Interface1
End Interface
如果属性缺少属性 System.AttributeUsage,则可以将属性放置在任何目标上(等效于 AttributeTargets.All)。 该 System.AttributeUsage 特性具有一个变量初始值设定项, AllowMultiple它指定是否可为给定声明多次指定指示的属性。 如果 AllowMultiple 属性是 True,则它是 多用属性类,并且可以在声明上多次指定。 如果 AllowMultiple 属性是 False 或未指定某个属性,则它是 一个单用属性类,并且可以在声明上最多指定一次。
以下示例定义一个名为 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
该示例显示了一个类声明,其中包含两个属性的 Author 用法:
<Author("Maria Hammond"), Author("Ramesh Meyyappan")> _
Class Class1
End Class
该 System.AttributeUsage 特性具有公共实例变量, Inherited该变量指定在基类型上指定时属性是否也由派生自此基类型的类型继承。
Inherited如果未初始化公共实例变量,则使用默认值False。 属性和事件不会继承属性,尽管属性和事件定义的方法确实会继承属性。 接口不继承属性。
如果对派生类型同时继承和指定单用属性,则派生类型中指定的属性将替代继承的属性。 如果多用属性在派生类型上继承和指定,则这两个属性都在派生类型上指定。 例如:
<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
特性的位置参数由特性类的公共构造函数的参数定义。 位置参数必须 ByVal 且不能指定 ByRef。 公共实例变量和属性由属性类的公共读写属性或实例变量定义。 可在位置参数和公共实例变量和属性中使用的类型仅限于属性类型。 如果类型为下列类型之一,则为属性类型:
除和
Decimal. 之外Date的任何基元类型。Object类型。System.Type类型。一个枚举类型,前提是它及其嵌套的类型(如果有)具有
Public辅助功能。此列表中的一个类型之一的一维数组。
属性块
属性在 属性块中指定。 每个属性块都用尖括号(“)<>分隔,可以在属性块或多个属性块的逗号分隔列表中指定多个属性。 指定属性的顺序不重要。 例如,属性块<A, B><B, A>和 <A> <B><B> <A>所有等效项。
Attributes
: AttributeBlock+
;
AttributeBlock
: LineTerminator? '<' AttributeList LineTerminator? '>' LineTerminator?
;
AttributeList
: Attribute ( Comma Attribute )*
;
Attribute
: ( AttributeModifier ':' )? SimpleTypeName
( OpenParenthesis AttributeArguments? CloseParenthesis )?
;
AttributeModifier
: 'Assembly' | 'Module'
;
不能在不支持的声明上指定属性,并且不能在属性块中多次指定单一使用属性。 下面的示例会导致错误,因为它尝试在接口Interface1上使用,并在声明Class1时多次尝试使用HelpString。
<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
特性由可选属性修饰符、属性名称、位置参数可选列表和变量/属性初始值设定项组成。 如果没有参数或初始值设定项,可以省略括号。 如果属性具有修饰符,则它必须位于源文件顶部的属性块中。
如果源文件包含文件顶部的属性块,该属性块指定将包含源文件的程序集或模块的属性,则属性块中的每个属性都必须以AssemblyModule或修饰符和冒号为前缀。
属性名称
特性的名称指定特性类。 按照约定,属性类以后缀 Attribute命名。 属性的使用可以包含或省略此后缀。 因此,与属性标识符对应的特性类的名称是标识符本身或限定标识符的串联。Attribute 当编译器解析属性名称时,它会追加 Attribute 到该名称并尝试查找。 如果该查找失败,编译器将尝试查找而不使用后缀。 例如,使用特性类 SimpleAttribute 可能会省略 Attribute 后缀,从而将名称缩短为 Simple:
<Simple> Class Class1
End Class
<Simple> Interface Interface1
End Interface
上面的示例在语义上等效于以下内容:
<SimpleAttribute> Class Class1
End Class
<SimpleAttribute> Interface Interface1
End Interface
通常,首选使用后缀 Attribute 命名的属性。 以下示例显示了两个命名 T 和 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
属性块 <T> 和属性块 <TAttribute> 都引用名为 TAttribute的属性类。 在删除类TAttribute的声明之前,不能T将其用作属性。
属性参数
特性的参数可能采用两种形式: 位置参数 和 实例变量/属性初始值设定项。 属性的任何位置参数都必须位于实例变量/属性初始值设定项之前。 位置参数由常量表达式、一维数组创建表达式或 GetType 表达式组成。 实例变量/属性初始值设定项由标识符组成,该标识符可以匹配关键字,后跟冒号和等号,并由常量表达式或 GetType 表达式终止。
给定具有特性类 T、位置参数列表 P和实例变量/属性初始值设定项列表 N的属性,这些步骤确定参数是否有效:
按照编译时处理步骤编译表单
New T(P)的表达式。 这会导致编译时错误或确定该构造T函数最适用于参数列表。如果在步骤 1 中确定的构造函数的参数不是属性类型或在声明站点上不可访问,则会发生编译时错误。
对于每个实例变量/属性初始值设定项
ArgN,让我们Name成为实例变量/属性初始值设定项Arg的标识符。Name必须标识其类型为属性类型的非Shared可Public写变量或无参数属性T。 如果没有T此类实例变量或属性,则会发生编译时错误。
例如:
<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
类型参数不能在属性参数中的任何位置使用。 但是,可以使用构造类型:
<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
;