编写自定义属性

若要设计自定义属性,无需学习许多新的概念。 如果你熟悉面向对象的编程,并且知道如何设计类,那么你已经具备大部分所需知识。 自定义属性直接或间接派生自 System.Attribute 类的传统类。 与传统类一样,自定义特性包含用于存储和检索数据的方法。

正确设计自定义特性的主要步骤如下:

本节描述上述各个步骤,并以 自定义特性的示例结束本节的描述。

应用 AttributeUsageAttribute

自定义属性声明以 System.AttributeUsageAttribute 属性开头,定义特性类的一些主要特征。 例如,你可以指定其他类是否可以继承你的属性,或者此属性可以应用到哪些元素。 以下代码片段演示 AttributeUsageAttribute 的使用方法:

[AttributeUsage(AttributeTargets::All, Inherited = false, AllowMultiple = true)]
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
<AttributeUsage(AttributeTargets.All, Inherited:=False, AllowMultiple:=True)>
Public Class SomeClass
    Inherits Attribute
    '...
End Class

AttributeUsageAttribute 包含下列三个成员,它们对创建自定义属性非常重要:AttributeTargetsInheritedAllowMultiple

AttributeTargets 成员

上述示例中指定 AttributeTargets.All,表示此属性可应用于所有程序元素。 或者,你可指定 AttributeTargets.ClassAttributeTargets.Method,前者表示你的特性仅可适用于一个类,后者表示你的特性仅可应用于一种方法。 所有程序元素都可以通过这种方式使用自定义特性来标记,以对其进行描述。

你还可传递多个 AttributeTargets 值。 以下代码段指定可以将自定义属性应用于任何类或方法:

[AttributeUsage(AttributeTargets::Class | AttributeTargets::Method)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
    Inherits Attribute
    '...
End Class

Inherited 属性

AttributeUsageAttribute.Inherited 属性指明要对其应用属性的类的派生类能否继承此属性。 此属性使用 true(默认值)或 false 标志。 在以下示例中,MyAttribute 的默认 Inherited 值为 true,而 YourAttributeInherited 值为 false

// This defaults to Inherited = true.
public ref class MyAttribute : Attribute
{
    //...
};

[AttributeUsage(AttributeTargets::Method, Inherited = false)]
public ref class YourAttribute : Attribute
{
    //...
};
// This defaults to Inherited = true.
public class MyAttribute : Attribute
{
    //...
}

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class YourAttribute : Attribute
{
    //...
}
' This defaults to Inherited = true.
Public Class MyAttribute
    Inherits Attribute
    '...
End Class

<AttributeUsage(AttributeTargets.Method, Inherited:=False)>
Public Class YourAttribute
    Inherits Attribute
    '...
End Class

然后,将这两个属性应用于基类 MyClass 中的方法:

public ref class MyClass
{
public:
    [MyAttribute]
    [YourAttribute]
    virtual void MyMethod()
    {
        //...
    }
};
public class MyClass
{
    [MyAttribute]
    [YourAttribute]
    public virtual void MyMethod()
    {
        //...
    }
}
Public Class MeClass
    <MyAttribute>
    <YourAttribute>
    Public Overridable Sub MyMethod()
        '...
    End Sub
End Class

最后,从基类 YourClass 中继承类 MyClass。 方法 MyMethod 显示 MyAttribute,但不显示 YourAttribute

public ref class YourClass : MyClass
{
public:
    // MyMethod will have MyAttribute but not YourAttribute.
    virtual void MyMethod() override
    {
        //...
    }

};
public class YourClass : MyClass
{
    // MyMethod will have MyAttribute but not YourAttribute.
    public override void MyMethod()
    {
        //...
    }
}
Public Class YourClass
    Inherits MeClass
    ' MyMethod will have MyAttribute but not YourAttribute.
    Public Overrides Sub MyMethod()
        '...
    End Sub

End Class

AllowMultiple 属性

AttributeUsageAttribute.AllowMultiple 属性指明元素能否包含属性的多个实例。 如果设置为 true,则允许多个实例。 如果设置为 false(默认值),那么只允许一个实例。

在以下示例中,MyAttribute 的默认 AllowMultiple 值为 false,而 YourAttribute 的值为 true

//This defaults to AllowMultiple = false.
public ref class MyAttribute : Attribute
{
};

[AttributeUsage(AttributeTargets::Method, AllowMultiple = true)]
public ref class YourAttribute : Attribute
{
};
//This defaults to AllowMultiple = false.
public class MyAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
' This defaults to AllowMultiple = false.
Public Class MyAttribute
    Inherits Attribute
End Class

<AttributeUsage(AttributeTargets.Method, AllowMultiple:=true)>
Public Class YourAttribute
    Inherits Attribute
End Class

当应用这些特性的多个实例时, MyAttribute 会生成编译器错误。 以下代码示例显示 YourAttribute 的有效用法以及 MyAttribute的无效用法:

public ref class MyClass
{
public:
    // This produces an error.
    // Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    void MyMethod()
    {
        //...
    }

    // This is valid.
    [YourAttribute]
    [YourAttribute]
    void YourMethod()
    {
        //...
    }
};
public class MyClass
{
    // This produces an error.
    // Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    public void MyMethod()
    {
        //...
    }

    // This is valid.
    [YourAttribute]
    [YourAttribute]
    public void YourMethod()
    {
        //...
    }
}
Public Class MyClass
    ' This produces an error.
    ' Duplicates are not allowed.
    <MyAttribute>
    <MyAttribute>
    Public Sub MyMethod()
        '...
    End Sub

    ' This is valid.
    <YourAttribute>
    <YourAttribute>
    Public Sub YourMethod()
        '...
    End Sub
End Class

如果 AllowMultiple 属性和 Inherited 属性都设置为 true,从另一个类继承的类可以继承一个属性,并具有在同一个子类中应用相同属性的另一个实例。 如果 AllowMultiple 设置为 false,则父类中的所有特性的值将被子类中同一特性的新实例覆盖。

声明特性类

应用 AttributeUsageAttribute 以后,开始定义属性的细节。 特性类的声明类似于传统类的声明,如以下代码所示:

[AttributeUsage(AttributeTargets::Method)]
public ref class MyAttribute : Attribute
{
    // . . .
};
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
    // . . .
}
<AttributeUsage(AttributeTargets.Method)>
Public Class MyAttribute
    Inherits Attribute
    ' . . .
End Class

此特性定义说明了以下几点:

  • 特性类必须声明为公共类。

  • 按照约定,特性类的名称以单词 Attribute结束。 尽管没有要求,但仍建议执行此约定以保证可读性。 应用特性时,可以选择是否包含单词 Attribute。

  • 所有特性类必须直接或间接从 System.Attribute 类继承。

  • 在 Microsoft Visual Basic 中,所有自定义特性类必须具有 System.AttributeUsageAttribute 特性。

声明构造函数

类似于传统类,特性是通过构造函数初始化的。 下面的代码段阐明了典型的特性构造函数。 此公共构造函数采用一个参数,并设置一个等于其值的成员变量。

MyAttribute(bool myvalue)
{
    this->myvalue = myvalue;
}
public MyAttribute(bool myvalue)
{
    this.myvalue = myvalue;
}
Public Sub New(myvalue As Boolean)
    Me.myvalue = myvalue
End Sub

可以重载此构造函数以适应值的各种组合。 如果你还为自定义特性类定义了 属性 ,则在初始化该特性时可以使用命名参数和定位参数的组合。 通常情况下,将所有必选的参数定义为定位参数,将所有可选的参数定义为命名参数。 在这种情况下,没有必需的参数就无法初始化属性。 其他所有参数都是可选参数。

注意

在 Visual Basic 中,特性类的构造函数不应使用 ParamArray 参数。

下面的代码示例显示如何使用可选和必选参数应用使用上例中的构造函数的特性。 该示例假定特性有一个必选的布尔值和一个可选的字符串属性。

// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public ref class SomeClass
{
    //...
};
// One required (positional) parameter is applied.
[MyAttribute(false)]
public ref class SomeOtherClass
{
    //...
};
// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public class SomeClass
{
    //...
}
// One required (positional) parameter is applied.
[MyAttribute(false)]
public class SomeOtherClass
{
    //...
}
' One required (positional) and one optional (named) parameter are applied.
<MyAttribute(false, OptionalParameter:="optional data")>
Public Class SomeClass
    '...
End Class

' One required (positional) parameter is applied.
<MyAttribute(false)>
Public Class SomeOtherClass
    '...
End Class

声明属性

如果你想要定义一个命名参数,或者提供一种简单的方法来返回由特性存储的值,请声明 属性。 应将特性的属性声明为公共实体,此公告实体包含将返回的数据类型的描述。 定义将保存属性值的变量,并将此变量与 getset 方法相关联。 以下代码示例说明如何在属性中实现一个简单属性:

property bool MyProperty
{
    bool get() {return this->myvalue;}
    void set(bool value) {this->myvalue = value;}
}
public bool MyProperty
{
    get {return this.myvalue;}
    set {this.myvalue = value;}
}
Public Property MyProperty As Boolean
    Get
        Return Me.myvalue
    End Get
    Set
        Me.myvalue = Value
    End Set
End Property

自定义特性的示例

本节内容结合了前面的信息,显示如何设计一个属性来记录有关一段代码的作者的信息。 本示例中的特性存储了编程人员的姓名和级别,以及是否已检查此代码的信息。 它使用三个私有变量来存储要保存的实际值。 每个变量用获取和设置这些值的公共属性表示。 最后,使用两个必需的参数定义构造函数:

[AttributeUsage(AttributeTargets::All)]
public ref class DeveloperAttribute : Attribute
{
    // Private fields.
private:
    String^ name;
    String^ level;
    bool reviewed;

public:
    // This constructor defines two required parameters: name and level.

    DeveloperAttribute(String^ name, String^ level)
    {
        this->name = name;
        this->level = level;
        this->reviewed = false;
    }

    // Define Name property.
    // This is a read-only attribute.

    virtual property String^ Name
    {
        String^ get() {return name;}
    }

    // Define Level property.
    // This is a read-only attribute.

    virtual property String^ Level
    {
        String^ get() {return level;}
    }

    // Define Reviewed property.
    // This is a read/write attribute.

    virtual property bool Reviewed
    {
        bool get() {return reviewed;}
        void set(bool value) {reviewed = value;}
    }
};
[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : Attribute
{
    // Private fields.
    private string name;
    private string level;
    private bool reviewed;

    // This constructor defines two required parameters: name and level.

    public DeveloperAttribute(string name, string level)
    {
        this.name = name;
        this.level = level;
        this.reviewed = false;
    }

    // Define Name property.
    // This is a read-only attribute.

    public virtual string Name
    {
        get {return name;}
    }

    // Define Level property.
    // This is a read-only attribute.

    public virtual string Level
    {
        get {return level;}
    }

    // Define Reviewed property.
    // This is a read/write attribute.

    public virtual bool Reviewed
    {
        get {return reviewed;}
        set {reviewed = value;}
    }
}
<AttributeUsage(AttributeTargets.All)>
Public Class DeveloperAttribute
    Inherits Attribute
    ' Private fields.
    Private myname As String
    Private mylevel As String
    Private myreviewed As Boolean

    ' This constructor defines two required parameters: name and level.

    Public Sub New(name As String, level As String)
        Me.myname = name
        Me.mylevel = level
        Me.myreviewed = False
    End Sub

    ' Define Name property.
    ' This is a read-only attribute.

    Public Overridable ReadOnly Property Name() As String
        Get
            Return myname
        End Get
    End Property

    ' Define Level property.
    ' This is a read-only attribute.

    Public Overridable ReadOnly Property Level() As String
        Get
            Return mylevel
        End Get
    End Property

    ' Define Reviewed property.
    ' This is a read/write attribute.

    Public Overridable Property Reviewed() As Boolean
        Get
            Return myreviewed
        End Get
        Set
            myreviewed = value
        End Set
    End Property
End Class

可以采用以下任一种方法,使用全称 DeveloperAttribute 或缩写名称 Developer 应用此属性:

[Developer("Joan Smith", "1")]

-or-

[Developer("Joan Smith", "1", Reviewed = true)]
[Developer("Joan Smith", "1")]

-or-

[Developer("Joan Smith", "1", Reviewed = true)]
<Developer("Joan Smith", "1")>

-or-

<Developer("Joan Smith", "1", Reviewed := true)>

第一个示例显示仅应用了必需的命名参数的特性。 第二个示例显示同时应用了必需的参数和可选参数的特性。

请参阅