Visual Basic 中的属性

Visual Basic 语言使程序员能够指定声明的修饰符,这些修饰符表示有关要声明的实体的信息。 例如,将类方法与修饰符 PublicProtected、、FriendProtected 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 命名的属性。 以下示例显示了两个命名 TT``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的属性,这些步骤确定参数是否有效:

  1. 按照编译时处理步骤编译表单 New T(P)的表达式。 这会导致编译时错误或确定该构造 T 函数最适用于参数列表。

  2. 如果在步骤 1 中确定的构造函数的参数不是属性类型或在声明站点上不可访问,则会发生编译时错误。

  3. 对于每个实例变量/属性初始值设定项ArgN,让我们Name成为实例变量/属性初始值设定项Arg的标识符。 Name 必须标识其类型为属性类型的非SharedPublic 写变量或无参数属性 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
    ;