第 18 章 定制特性

第 18 章 定制特性

本章内容

利用定制特性,可宣告式地为自己的代码构造添加注解来实现特殊功能。定制特性允许为几乎每一个元数据表记录项定义和应用信息。

18.1 使用定制特性

C# 只允许将特性应用于定义以下任何目标元素的源代码:程序集、模块、类型(类、结构、枚举、接口、委托)、字段、方法(含构造器)、方法参数、方法返回值、属性、事件和泛型类型参数。

定制特性其实是一个类型的实例。定制特性类必须直接或间接从公共抽象类 System.Attribute派生。

18.2 定义自己的特性类

1
2
3
4
5
6
7
namespace System {
[AttributeUsage(AttributeTargets.Enum, Inherited = false)]
public class FlagsAttribute : System.Attribute {
public FlagsAttribute() {
}
}
}

所有非抽象特性至少要包含一个公共构造器。

18.3 特性构造器和字段/属性数据类型

定义特性类的实例构造器、字段和属性时,可供选择的数据类型并不多。具体地说,只允许 BooleanCharByteSByteInt16UInt16Int32UInt32Int64UInt64SingleDoubleStringTypeObject或枚举类型。此外,可使用上述任意类型的一维 0 基数组。但应尽量避免使用数组,因为对于定制特性,如果它的构造器要获取数组作为参数,就会失去与 CLS 的相容性。

18.4 检测定制特性

代码利用一种称为反射的技术检测特性的存在。
假定你是 Microsoft 的员工,负责实现 EnumToString 方法,你会像下面这样实现它:

1
2
3
4
5
6
7
8
9
10
11
12
public override String ToString() {

// 枚举类型是否应用了 FlagsAttribute 类型的实例 ?
if (this.GetType().IsDefined(typeof(FlagsAttribute), false)) {
// 如果是,就执行代码,将值视为一个位标志枚举类型
...
} else {
// 如果不是,就执行代码,将值视为一个普通枚举类型
...
}
...
}

18.5 两个特性实例的相互匹配

18.7 条件特性类

应用了 System.Diagnostics.ConditionalAttribute 的特性类称为条件特性类。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// #define TEST
#define VERIFY

using System;
using System.Diagnostics;

[Conditional("TEST")]
[Conditional("VERIFY")]
public sealed class CondAttribute : Attribute {
}

[Cond]
public sealed class Program {
public static void Main() {
Console.WriteLine("ConAttribute is {0}applied to Program type.",
Attribute.IsDefined(typeof(Program), typeof(CondAttribute)) ? "" : "not");
}
}

编译器如果发现向目标元素应用了 CondAttribute 的实例,那么当含有目标元素的代码编译时,只有在定义 TESTVERIFY 符号的前提下,编译器才会在元数据中生成特性信息。不过,特性类的定义元数据和实现仍存在于程序集中。