特性的定义:公共语言运行时允许添加类似关键字的描述声明,叫做attribute,它对程序中的元素进行标注,如类型、字段、方法、和属性等。attribute和.NetFramework文件的元数据保存在一起,可以用来在运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。
定制特性attribute,本质上是一个雷,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。
一、特性的通用规则。
1、定制特性可以应用的目标元素包括:程序集(assembly)、模块(module)、类型(type)、属性(proprety)、事件(event)、字段(field)、方法(method)、参数(param)、返回值(return),不外乎这些。
2、定制特性以[,]的形式展现,放在紧挨的元素上,多个特性可以应用于同一元素,特性间以逗号隔开,以下表达规则都是有效的:
[AttributeUsage][Flags]、[AttributeUsage,Flags]、[Flags,AttributeUsageAttribute]、[AttributeUsage(),FlagesAttribute()]
3、attribute示例,是在编译期进行初始化,而不是运行期。
4、C#允许以指定的前缀来表示特性所应用的目标元素,建议这样来处理,因为显示处理可以消除可能带来的二义性。如:
[assembly:MySelfAttribute()] [module:MySelfAttribute("小王",27)] public class AttributeArea { }
5、定制特性类型,必须直接或者间接继承自System.Attribute类,而且该类型必须有共有构造函数来创建其实例。
6、所有自定义的特性名称都应该有个Attribute后缀,这是习惯性约定。
7、定义特性也可以应用在其他定制特性上,因为定制特性本身也是一个类,遵守类的公有规则。例如很多时候我们的自定义定制特性会应用AttributeUsageAttribute特性,来控制如何应用新定义的特性。如:
[AttributeUsageAttribute(AttributeTargets.All, AllowMultiple = true, Inherited = true)] class MyNewAttribute : System.Attribute { }
8、定制特性不会影响应用元素的任何功能,只是约定了该元素具有的特质。
9、所有非抽象特性必须具有public访问限制。
10、特性常用语编译器指令,突破#define,#undefine,#if,#endif的限制,而且更加灵活。
11、定制特性常用于在运行期获的代码注释信息,以附加信息来优化调试。
12、定制特性可以应用在某些设计模式中,如工厂模式,根据附加信息来决定执行的逻辑分支,降低了系统代码的耦合度。
13、定制特性还常用于位标记,非托管函数标记、方法废弃标记等其他方面。
二、特性的应用
常用特性,也就是.Net已经提供的固有特性,事实上.Net框架中已经提供了丰富的固有特性由我们选用,下面选出一些常用和经典的做简单讨论。
1、AttributeUsage
AttributeUsage特性用于控制如何应用自定义特性到目标元素。其中属性有AttributeTargets枚举,AllowMultiple、Inherited、ValidOn等。
AttributeUsage类是另外一个预定义特性类,它帮助我们控制我们自己的定制特性的使用。它描述了一个定制特性如和被使用。
AttributeUsage有三个属性,我们可以把它放置在定制属性前面。第一个属性是:
ValidOn
通过这个属性,我们能够定义定制特性应该在何种程序实体前放置。一个属性可以被放置的所有程序实体在AttributeTargets enumerator中列出。通过OR操作我们可以把若干个AttributeTargets值组合起来。
AllowMultiple
这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。
Inherited
我们可以使用这个属性来控制定制特性的继承规则。它标记了我们的特性能否被继承。
2、Flags
Flags特性用来将枚举数值看作位标记,而非单独的数值。
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Animal animals = Animal.Dog | Animal.Cat; Console.WriteLine(animals.ToString()); //输出 Dog,Cat 如果没有Flags特性,这里的结果将是"3" Console.ReadKey(); } } [Flags] enum Animal { Dog = 0x0001, Cat = 0x0002, Duck = 0x0004, Chicken = 0x0008 } }
3、DllImport
DllImport特性,可以让我们调用非托管代码,所以我们可以使用DllImport特性引入对Win32 API函数的调用。
4、Serializable
Serializable特性表名了应用的元素可以被序列化。
5、Conditional
Conditional特性,用于条件编译,在调试时使用。
三、自定义特性
既然attribute本质上就是一个类,那么我们就可以自定义更特定的attribute来满足个性化需求,只要遵守上述的多条规则,实现一个自定义特性其实是很容易的。
[Test("标记参数")] public static void CannotRun() { } [AttributeUsage(AttributeTargets.All, Inherited = true)] public class TestAttribute : System.Attribute { public TestAttribute(string message) { throw new Exception(); Console.WriteLine(message); } public void RunTest() { Console.WriteLine("这里是自定义特性的代码!"); } }