编写自定义特性

若要设计自己的自定义特性,不必掌握许多新的概念。 如果熟悉面向对象的编程,并且知道如何设计类,就已具备了所需的大部分知识。 自定义特性实质上是直接或间接派生自 System.Attribute 的传统类。 与传统类一样,自定义特性也包含存储和检索数据的方法。

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

  • 应用 AttributeUsageAttribute

  • 声明特性类

  • 声明构造函数

  • 声明属性

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

应用 AttributeUsageAttribute

自定义特性声明以 AttributeUsageAttribute 开始,它定义特性类的一些主要特征。 例如,可指定特性是否可被其他类继承,或指定特性可应用到哪些元素。 下列代码段说明了如何使用 AttributeUsageAttribute

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

System.AttributeUsageAttribute 包含三个对创建自定义特性具有重要意义的成员:AttributeTargets、Inherited 和 AllowMultiple。

AttributeTargets 成员

在前面的示例中指定了 AttributeTargets.All,指示该特性可以应用到所有程序元素。 还可以指定 AttributeTargets.Class,指示特性只可以应用于某个类;或指定 AttributeTargets.Method,指示特性只可以应用于某个方法。 所有程序元素都可通过这种方式由自定义特性标记,以便对其进行描述。

您还可以传递 AttributeTargets 的多个实例。 下列代码段指定自定义特性可应用到任何类或方法。

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

继承属性

Inherited 属性指示特性是否可由应用了该特性的类的派生类来继承。 该属性采用 true(默认值)或 false 标志。 例如,在下面的代码示例中,MyAttribute 的默认 Inherited 值为 true,而 YourAttribute 的 Inherited 值为 false

' This defaults to Inherited = true.
Public Class MyAttribute
    Inherits Attribute
    '...
End Class

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

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

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

然后,这两个特性应用到基类 MyClass 中的某个方法。

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

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

Public Class YourClass
    Inherits MeClass
    ' MyMethod will have MyAttribute but not YourAttribute.
    Public Overrides Sub MyMethod()
        '...
    End Sub

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

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

};

AllowMultiple 属性

AllowMultiple 属性指示元素中是否可存在特性的多个实例。 如果设置为 true,则允许存在多个实例;如果设置为 false(默认值),则只允许存在一个实例。

在下面的代码示例中,MyAttribute 的默认 AllowMultiple 值为 false,而 YourAttribute 的对应值为 true

' This defaults to AllowMultiple = false.
Public Class MyAttribute
    Inherits Attribute
End Class

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

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

[AttributeUsage(AttributeTargets::Method, AllowMultiple = true)]
public ref class YourAttribute : Attribute
{
};

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

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
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 ref class MyClass
{
public:
    // This produces an error.
    // Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    void MyMethod()
    {
        //...
    }

    // This is valid.
    [YourAttribute]
    [YourAttribute]
    void YourMethod()
    {
        //...
    }
};

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

声明特性类

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

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

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

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

  • 按照约定,特性类的名称以单词 Attribute 结尾。 虽然并不要求这样,但出于可读性目的,建议采用此约定。 应用特性时,可以选择是否包含 Attribute 一词。

  • 所有特性类都必须直接或间接地继承自 System.Attribute

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

声明构造函数

用构造函数初始化特性的方法与对待传统类的方法相同。 下列代码段阐释了典型的特性构造函数。 该公共构造函数采用一个参数,并将其值设置为与成员变量相等。

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

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

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

' 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
// 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 ref class SomeClass
{
    //...
};
// One required (positional) parameter is applied.
[MyAttribute(false)]
public ref class SomeOtherClass
{
    //...
};

声明属性

如果需要定义命名参数,或者要提供一种容易的方法来返回由特性存储的值,请声明属性。 应将特性的属性声明为带有要返回的数据类型说明的公共实体。 定义将保存属性值的变量,并将该变量与 get 方法和 set 方法关联。 下面的代码示例说明如何在特性中实现一个简单的属性。

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

可通过下列方法之一,使用全名 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)]

第一个示例显示只应用有必选命名参数的特性,第二个示例显示同时应用有必选参数和可选参数的特性。

请参见

参考

System.Attribute

AttributeUsageAttribute

概念

利用特性扩展元数据