1、什么是Atrribute
首先,我们肯定Attribute是一个类,下面是msdn文档对它的描述:
公共语言运行时允许你添加类似关键字的描述声明,叫做attributes,它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NETFramework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。
在.NET中,Attribute被用来处理多种问题,比如序列化、程序的安全特征、防止即时编译器对程序代码进行优化从而代码容易调试等等。下面,我们先来看几个在.NET中标准的属性的使用,稍后我们再回过头来讨论Attribute这个类本身。(文中的代码使用C#编写,但同样适用所有基于.NET的所有语言)
2、Attribute作为编译器的指令
在C#中存在着一定数量的编译器指令,如:#define DEBUG, #undefine DEBUG,#if等。这些指令专属于C#,而且在数量上是固定的。而Attribute用作编译器指令则不受数量限制。比如下面的三个Attribute:
Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。
DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。
Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。
下面的代码演示了上述三个属性的使用:
2
3usingSystem;
4usingSystem.Runtime.InteropServices;
5usingSystem.Diagnostics;
6
7namespaceAttributeDemo
8{
9class MainProgramClass
10{
11
12[DllImport("User32.dll")]
13public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
14
15static void Main(string[] args)
16{
17DisplayRunningMessage();
18DisplayDebugMessage();
19
20MessageBox(0,"Hello","Message",0);
21
22Console.ReadLine();
23}
24
25[Conditional("DEBUG")]
26private static void DisplayRunningMessage()
27{
28Console.WriteLine("开始运行Main子程序。当前时间是"+DateTime.Now);
29}
30
31[Conditional("DEBUG")]
32//[Obsolete("Don't use Old method, use Newmethod", true)]
33[Obsolete]
34private static void DisplayDebugMessage()
35{
36Console.WriteLine("开始Main子程序");
37}
38}
39}
如果在一个程序元素前面声明一个Attribute,那么就表示这个Attribute被施加到该元素上,前面的代码,[DllImport]施加到MessageBox函数上,[Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。
根据上面涉及到的三个Attribute的说明,我们可以猜到程序运行的时候产生的输出:DllImportAttribute表明了MessageBox是User32.DLL中的函数,这样我们就可以像内部方法一样调用这个函数。
重要的一点就是Attribute就是一个类,所以DllImport也是一个类,Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。Attribute实例化的时候根据该Attribute类的设计可以带参数,也可以不带参数,比如DllImport就带有"User32.dll"的参数。Conditional对满足参数的定义条件的代码进行编译,如果没有定义DEBUG,那么该方法将不被编译,读者可以把#defineDEBUG一行注释掉看看输出的结果(release版本,在Debug版本中Conditional的debug总是成立的)。Obsolete表明了DispalyDebugMessage方法已经过时了,它有一个更好的方法来代替它,当我们的程序调用一个声明了Obsolete的方法时,那么编译器会给出信息,Obsolete还有其他两个重载的版本。大家可以参考msdn中关于的ObsoleteAttribute类的描述。
3、Attribute类
除了.NET提供的那些Attribute派生类之外,我们可以自定义我们自己的Attribute,所有自定义的Attribute必须从Attribute类派生。现在我们来看一下Attribute类的细节:
protected Attribute(): 保护的构造器,只能被Attribute的派生类调用。
三个静态方法:
static AttributeGetCustomAttribute():这个方法有8种重载的版本,它被用来取出施加在类成员上指定类型的Attribute。
static Attribute[] GetCustomAttributes():这个方法有16种重载版本,用来取出施加在类成员上指定类型的Attribute数组。
static boolIsDefined():由八种重载版本,看是否指定类型的定制attribute被施加到类的成员上面。
实例方法:
bool IsDefaultAttribute(): 如果Attribute的值是默认的值,那么返回true。
bool Match():表明这个Attribute实例是否等于一个指定的对象。
公共属性: TypeId:得到一个唯一的标识,这个标识被用来区分同一个Attribute的不同实例。
我们简单地介绍了Attribute类的方法和属性,还有一些是从object继承来的。这里就不列出来了。
下面介绍如何自定义一个Attribute:自定义一个Attribute并不需要特别的知识,其实就和编写一个类差不多。自定义的Attribute必须直接或者间接地从Attribute这个类派生,如:
public MyCustomAttribute : Attribute { ... }
这里需要指出的是Attribute的命名规范,也就是你的Attribute的类名+"Attribute",当你的Attribute施加到一个程序的元素上的时候,编译器先查找你的Attribute的定义,如果没有找到,那么它就会查找“Attribute名称"+Attribute的定义。如果都没有找到,那么编译器就报错。
4、定义或控制特性的使用
对于一个自定义的Attribute,你可以通过AttributeUsage的Attribute来限定你的Attribute所施加的元素的类型。代码形式如下:
[AttriubteUsage(参数设置)] public自定义Attribute : Attribute { ... }
非常有意思的是,AttributeUsage本身也是一个Attribute,这是专门施加在Attribute类的Attribute.AttributeUsage自然也是从Attribute派生,它有一个带参数的构造器,这个参数是AttributeTargets的枚举类型。下面是AttributeTargets的定义:
1publicenum AttributeTargets2{
3All=16383,
4Assembly=1,
5Module=2,
6Class=4,
7Struct=8,
8Enum=16,
9Constructor=32,
10Method=64,
11Property=128,
12Field=256,
13Event=512,
14Interface=1024,
15Parameter=2048,
16Delegate=4096,
17ReturnValue=8192
18}
作为参数的AttributeTarges的值允许通过“或”操作来进行多个值得组合,如果你没有指定参数,那么默认参数就是All 。AttributeUsage除了继承Attribute 的方法和属性之外,还定义了以下三个属性:
AllowMultiple: 读取或者设置这个属性,表示是否可以对一个程序元素施加多个Attribute 。
Inherited:读取或者设置这个属性,表示是否施加的Attribute 可以被派生类继承或者重载。
ValidOn: 读取或者设置这个属性,指明Attribute 可以被施加的元素的类型。
下面让我们来做一些实际的东西。我们将会在Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。
1usingSystem;2[AttributeUsage(AttributeTargets.Class),AllowMultiple =false,
3Inherited= false ]
4publicclass HelpAttribute : Attribute
5{
6publicHelpAttribute(StringDescription_in)
7{
8this.description= Description_in;
9}
10protectedString description;
11publicString Description
12{
13get
14{
15returnthis.description;
16}
17}
18}
先让我们来看一下AttributeTargets.Class。它规定了Help特性只能被放在class的前面。这也就意味着下面的代码将会产生错误:
1[Help("thisis a do-nothing class")]2publicclass AnyClass
3{
4[Help("thisis a do-nothing method")] //error
5publicvoid AnyMethod()
6{
7}
8}
编译器报告错误如下:
AnyClass.cs: Attribute 'Help' is not valid on this declarationtype.
It is valid on 'class' declarations only.
我们可以使用AttributeTargets.All来允许Help特性被放置在任何程序实体前。可能的值是:
Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,
Parameter,Delegate。
All = Assembly | Module |Class | Struct | Enum | Constructor | Method | Property | Field |Event | Interface | Parameter | Delegate,
ClassMembers = Class | Struct| Enum | Constructor | Method | Property | Field | Event | Delegate| Interface )
下面考虑一下AllowMultiple = false。它规定了特性不能被重复放置多次。
1[Help("thisis a do-nothing class")]2[Help("itcontains a do-nothing method")]
3publicclass AnyClass
4{
5[Help("thisis a do-nothing method")] //error
6publicvoid AnyMethod()
7{
8}
9}
它产生了一个编译期错误。
AnyClass.cs: Duplicate 'Help' attribute
Ok,现在我们来讨论一下最后的这个属性。Inherited, 表明当特性被放置在一个基类上时,它能否被派生类所继承。
1[Help("BaseClass")]2publicclass Base
3{
4}
5
6publicclass Derive : Base
7{
8}
9
这里会有四种可能的组合:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false,Inherited = false ]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true,Inherited = false ]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false,Inherited = true ]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true,Inherited = true ]
第一种情况:
如果我们查询(Query)(稍后我们会看到如何在运行期查询一个类的特性)Derive类,我们将会发现Help特性并不存在,因为inherited属性被设置为false。
第二种情况:
和第一种情况相同,因为inherited也被设置为false。
第三种情况:
为了解释第三种和第四种情况,我们先来给派生类添加点代码:
1[Help("BaseClass")]2publicclass Base
3{
4}
5[Help("DeriveClass")]
6publicclass Derive : Base
7{
8}
现在我们来查询一下Help特性,我们只能得到派生类的属性,因为inherited被设置为true,但是AllowMultiple却被设置为false。因此基类的Help特性被派生类Help特性覆盖了。
第四种情况:
在这里,我们将会发现派生类既有基类的Help特性,也有自己的Help特性,因为AllowMultiple被设置为true。
至此,我们介绍了有关Attribute类和它们的代码格式。你一定想知道到底如何在你的应用程序中使用Attribute,如果仅仅是前面介绍的内容,还是不足以说明Attribute有什么实用价值的话,那么从后面的章节开始我们将介绍几个Attribute的不同用法,相信你一定会对Attribute有一个新的了解。