定制特性(custom attribute)任何人都可以使用定制特性来定义一下信息,并将这些信息用于几乎所有的元数据表项上,然后在运行时通过查询这些可扩展的元数据信息来动态地改变代码的执行方式。
1.使用定制特性
定制特性仅仅是为了目标元素提供关联附加信息的一种方式。编译器仅仅只是检测源代码中的定制特性,然后产生相应的元数据。
.NET框架(FCL)发布的时候带有很多预定义特性。例如System.FlagsAttribute,System.SerializeableAttribute等。
C#中,将定制特性放在紧挨着目标元素前的一个方括号[,]中,表示将该定制特性应用到目标元素上了。在VB中使用的是尖括号<,>
CLR允许将特性应用于任何可以在一个文件的元数据中表示的元素。特性经常被应用于一些数据定义表中的条目上:TypeDef(类,结构,枚举。接口,委托)、MethodDef(包括构造器)、ParamDef、FieldDef、PropertyDef、EventDef、AssembleyDef,以及ModuleDef。虽然很少见,但是特性也可以应用到元数据引用表中的条目上,例如AssemblyRef,ModuleRef,TypeRef,以及MemberRef。最后。定制特性也可以拥有到其他一些元数据中,例如安全许可。导出类型以及资源等。
但是,C#只允许我们将特性应用于定义一些构造的源代码中:程序集、模块、类型、字段、方法、方法参数、方法返回类型、属性,以及事件。允许我们使用前缀来表示特性所应用的目标元素。
例如[assembly:MyAttribute(1)]//应用于程序集
[module:MyAttribute(2)]//应用于模块上
[type:MyAttribute(3)]//应用于类型上
[event:MyAttribute(5)]//应用于事件上
[field:MyAttribute(6)]//应用于字段
[return:MyAttribute(7)]//应用于返回值
[method:MyAttribute(8)]//应用于方法上
[param:MyAttribute(9)]//应用于参数上
一个特性仅仅是一个类型的实例。要与通用语言规范(CLS)兼容,定制特性的类型必须直接或间接继承自System.Attribute。
该类型必须有一个公有构造器来创建它的实例,所以当我们在一个目标元素上应用一个特性时,其语法类似于调用类型的实例构造器。
一个特性类型的构造器参数被称为定制参数(positioanl parameter),该参数是强制性的,也就是说在应用特性时必须制定这样的参数。
设置字段或属性的“参数”被称为命名参数(named parameter),并且它们是可选的。当在一个目标元素应用多个特性时,它们的顺序可以任意。
2.定义自己的特性
特性都继承自System.Attribute
namespace system{
public class FlagsAttribute:System.Attribute
{
public FlagsAttribute()
{
}
}
所有的非抽象特性都必须具有public访问权限,并且根据约定。所有特性列席的名称都应该有一个“Attribute”后缀,所有的非抽象特性都必须包含至少一个公有构造器。
为了告诉编译器那些地方可以使用该特性,我们可以在我们定义的特性类型上应用一个System.AttributeUsageAttribute类的实例。
AttributeUsageAttribute类型有一个构造器,它允许我们传递标记类表示我们的特性可以应用的地方。System.AttributeTargets枚举类型在FCL中的定义如下:
[Flages,Serializeable]
public enum AttributeTargets
{
Assembley=0x0001,
Module=0x0002,
Class=0x0004,
Struct=0x0008,
Enum=0x0010,
Constructor=0x0020,
Method=0x0040,
Property=0x0080,
.
.
.
}
}
AttributeUsageAttribute的另一个属性Inherited,表示特性是否应该应用于派生类或派生方法上。
3.特性构造器与字段/属性的数据类型
可以在特性类型中定义飞静态的公有字段和属性,他们标示一些开发人员在应用特性类型实例时可以有选择地指定一些设置。
当定义一个特性类型的实例化构造器、字段和属性时,特码呢的类型被严格地限制在也贵很小的子集中。另外我们也可以使用任何这些类型的 一维数组。当我们使用一个特性时,我们必须传递又给和特性类中定义的类型相匹配的编译时常说表达式。
4.检测定制特性
如果我们定义了自己的特性类型,我们还必须实现某些代码来检测目标元素上是否应用了这些特性类型的实例,然后根据检测结果执行相应的代码路径。只有这样,定制特性才有意义。
System.Attribute类的一些方法
IsDefined 如果至少有一个指定的Atrribute的派生类型应用到目标元素上,该类型返回true.
GetCustomAttributes 该方法返回一个数组,其中的元素时指定的、应用在目标元素上的特性实例。
GetCustioAttribute 该方法返回又给指定的、应用在目标元素上的特性实例。
5.特性实例间的匹配
编写代码查看特性实例中字段的值(利用重写System.Attribute的Mathc方法)
6.伪定制特性(pseudo-custom attribute)
他们看起来和通常的特性很像,并且他们在源代码中的应用方式也和通常的特性一样,但是他们是以一种高度压缩的形象来存储的。