跳转至

特性⚓︎

一、特性概述⚓︎

特性(Attribute)是一种用于在程序运行时传递各种元素(例如类、方法、结构、枚举等)行为信息的声明性代码。使用特性可以将元数据(例如编译器指令、注释、描述、方法和类等信息)添加到程序中。

在 C# 中,特性具有以下属性:

  • 使用特性可以向程序中添加元数据,元数据是指程序中各种元素的相关信息,所有 .NET 程序中都包含一组指定的元数据;
  • 可以将一个或多个特性应用于整个程序、模块或者较小的程序元素(例如类和属性)中;
  • 特性可以像方法和属性一样接受自变量;
  • 程序可使用反射来检查自己的元数据或其他程序中的元数据。

.Net 提供了两种类型的特性,分别是预定义特性和自定义特性。

  1. 预定义特性
    • AttributeUsage :用于自定义特性,其中定义了可以应用特性的项目类型;
    • Conditional :用来标记一个方法,它的执行依赖于指定的预处理标识符;
    • Obsolete :用来标记废弃的程序,可以使用它来通知编译器放弃某个目标元素。
  2. 自定义特性
    • 按规则和需要使用 AttributeUsage 实现特性自定义。

Tip

如果你熟知 Java 语言,你可以认为它和 Java 中的元注解具有相同的功能。

二、预定义特性⚓︎

01 AttributeUsage⚓︎

使用 AttributeUsage 可以实现自定义特性,它的语法格式如下:

[AttributeUsage(
    AttributeTargets, 
    Inherited, 
    AllowMultiple)]

参数说明:

  1. AttributeTargets :用于指定自定义的特性应用的目标位置,AttributeTargets.ALL 表示对程序中的所有成员都可用,Class 表示应用于类,Method 表示应用于方法,Constructor 表示应用于构造器,Field 表示应用于字段,Property 表示应用于属性。可以同时指定多个 AttributeTargets,此时,应该使用符号 | 分隔开。
  2. Inherited :用于指定自定义的特性是否可以被派生类继承,取值为 true (默认)或 false。
  3. AllowMultiple :用于指定一个元素是否可以被标记多个自定义特性的实例,取值为 true 或 false(默认)。

02 Conditional⚓︎

使用 Conditional 用来标记一个方法,它的执行依赖于指定的预处理标识符。例如当值为 Debug 或 Trace 时,会在调试代码时显示变量的值。它的语法格式如下:

[Conditional(TAG)] // TAG 为预处理标识符

示例:

[Conditional("DEBUG")]
public static void Message(string msg)
{
    Console.WriteLine(msg);
}

03 Obsolete⚓︎

使用 Obsolete 用来标记程序段被弃用,例如当您需要使用一个新方法来替代类中的某个旧方法时,就可以使用该特性将旧方法标记为 obsolete(过时的)并来输出一条消息,来提示应该使用新方法代替旧方法。它的语法格式如下:

[Obsolete (Message,IsError,UrlFormat)]

参数说明:

  1. Message :描述性字符串,可以用来说明弃用以及原因。
  2. IsError :用来标识使用弃用的方法是一个错误,取值为 true 或 false (默认)。当为 true 时,若代码段被调用程序将会报错。
  3. UrlFormat :用于指定一个 URL ,可以是一个说明文档的链接。

示例:

[Obsolete("方法已弃用",true)]
public static void Test(){}

private static void Main(string[] args)
{
    Test(); // 编译器会发出提醒,强行运行会报错
}

三、自定义特性⚓︎

通过 AttributeUsage 可以实现自定义特性,自定义特性和自定义类一样简单,自定义特性就是一个传统类,这些类直接或间接派生自 System.Attribute 类,与传统类一样,包含存储和检索数据的方法。

自定义一个特性的步骤可以参考: 1. 声明自定义特性; 2. 构建自定义特性; 3. 在目标程序上应用自定义特性; 4. 通过反射访问特性。

Note

  1. 特性类必须声明为公共类。
  2. 按照约定,属性类的名称以单词 Attribute 结尾。虽然不是必需的,但为了可读性,建议使用此约定。
  3. 所有属性类必须直接或间接地继承 System.Attribute 类。

示例:

[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;}
    }
}

然后就可以使用该属性了:

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

-or-

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