Attributes是一种新的描述信息,我们既可以使用attributes来定义设计期信息(例如 帮助文件,文档的URL),还可以用attributes定义运行时信息(例如,使XML中的元素与类的成员字段关联起来)。我们也可以用attributes来创建一个“自描述”的组件。在这篇指南中我们将明白怎么创建属性并将其绑定至各种语言元素上,另外我们怎样在运行时环境下获取到attributes的一些信息。
使用预定义 Attributes
在c#中已有一小组预定义的attributes,在我们学习怎样创建自定义attributes前,先来了解下在我们的代码中使用那些预定义的attributes.
仔细看下该实例,在该实例中我们用到了”Obsolete”attribute,它标记了一个不该再被使用的语言元素(译者注:这里的元素为方法),该属性的第一个参数是string类型,它解释为什么该元素被荒弃,以及我们该使用什么元素来代替它。实际中,我们可以书写任何其它文本来代替这段文本。第二个参数是告诉编译器把依然使用这被标识的元素视为一种错误,这就意味着编译器会因此而产生一个警告。
当我们试图编译上面的上面的程序,我们会得到如下错误:
AnyClass.Old()' is obsolete: 'Don't use Old method, use New method'
开发自定义Attributes
现在我们即将了解怎么开发自定义的attributes。这儿有个小小处方,有它我们就可以学会创建自定义的attributes。
在C#中,我们的attribute类都派生于System.Attribute类 (A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration) ,我们就这么行动吧。
不管你是否相信我,就这样我们就已经创建了一个自定义
attribute 。现在就可以用它来装饰我们的类了,就像我们使用 obsolete attribute 一样。
注意:按惯例我们是用”Attribute“作为attribute类名的后缀,然而,当我们当我们把attribute绑定到某语言元素时,是不包含“Attribute“后缀的。编译器首先在System.Attribute 的继承类中查找该attribute,如果没有找到,编译器会把“Attribute“追加到该attribute的名字后面,然后查找它。
但是迄今为止,该attribute没有任何用处。为了使它有点用处,让我们在它里面加点东西吧。
在上面的例子中,我们在attribute类中添加了一个属性,在最后一节中,我们将在运行时查询该属性。 AttributeTargets.Class. 它规定这个help attribute 只能放置在语言元素”class”之上。这就意味着,下面的代码将会产生一个错误。
定义或控制自定义Attribute的用法
AttributeUsage 类是另一预定义类(译者注:attribute类本身用这个atrribute System.AttributeUsage来标记),它将帮助我们控制我们自定义attribute的用法,这就是,我们能为自定义的attribute类定义attributes。
它描述了一个自定义attribute类能被怎样使用。
AttributeUsage 提供三个属性,我们能将它们放置到我们的自定义attribute类上, 第一个特性是:
ValidOn
通过这个属性,我们能指定我们的自定义attribute可以放置在哪些语言元素之上。这组我们能把自定义attribute类放置其上的语言元素被放在枚举器AttributeTargets 中。我们可以使用bitwise(译者注:这个词不知道怎么翻译好,但他的意思是可以这么用:[AttributeUsage((AttributeTargets)4, AllowMultiple = false, Inherited = false )],4代表就是“class”元素,其它诸如1代表“assembly”,16383代表“all”等等)或者”.”操做符绑定几个AttributeTargets 值。(译者注:默认值为AttributeTargets.All)
AllowMultiple
该属性标识我们的自定义attribte能在同一语言元素上使用多次。(译者注:该属性为bool类型,默认值为false,意思就是该自定义attribute在同一语言元素上只能使用一次)
Inherited
我们可以使用该属性来控制我们的自定义attribute类的继承规则。该属性标识我们的自定义attribute是否可以由派生类继承。((译者注:该属性为bool类型,默认值为false,意思是不能继承)
让我们来做点实际的东西吧,我们将把AttributeUsage attribute 放置在我们的help attribute 上并在它的帮助下,我们来控制help attribute的用法。
首先我们注意
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
现在试着把它绑定到方法。 AttributeTargets.All 来允许 Help attribute 可以放置在任何预定义的语言元素上,那些可能的语言元素如下:
我们可以使用
下面我给出在VS2010中操作Attributes的综合性实例:
首先打开Visual Studio2010创建一个基于C#的ConsoleApplication工程Attributes:
在Program.cs文件里写入如下代码:
using System; using System.Reflection; using System.Collections; //该IsTestedAttribute类是一个用户定义的自定义属性类。 //它可应用于任何包括报关 //- 类型(结构,类,枚举,委托) //- 成员(方法,字段,事件,属性,索引器) public class IsTestedAttribute : Attribute { public override string ToString() { return "测试"; } } //该AuthorAttribute类是一个用户定义的属性类。 //它可应用于类声明只和结构。 //它需要一位不愿透露姓名的字符串参数(作者姓名)。 //它有一个可选的命名的参数的版本,它是int类型。 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class AuthorAttribute : Attribute { // 此构造函数指定命名参数的属性类。 public AuthorAttribute(string name) { this.name = name; this.version = 0; } //此属性是只读的(它没有固定的访问) //所以它不能作为一个参数来命名这个attribute.class使用。 public string Name { get { return name; } } //此属性是读写(它有一个set访问) //所以它可以作为一个命名参数时,使用这种使用 //类作为属性类.. public int Version { get { return version; } set { version = value; } } public override string ToString() { string value = "作者 : " + Name; if (version != 0) { value += " 版本 : " + Version.ToString(); } return value; } private string name; private int version; } //这里你附加AuthorAttribute用户定义的属性 // Account类。这位未透露姓名的字符串参数传递给 // AuthorAttribute类的构造函数创建时的属性。 [Author("Joe Programmer")] class Account { //此方法是附加IsTestedAttribute自定义属性。 [IsTested] public void AddOrder(Order orderToAdd) { orders.Add(orderToAdd); } private ArrayList orders = new ArrayList(); } //附加AuthorAttribute和IsTestedAttribute自定义属性这个类。 //注意使用Version命名的参数的AuthorAttribute。 [Author("Jane Programmer", Version = 2), IsTested()] class Order { //在这里添加内容 ... } class MainClass { private static bool IsMemberTested(MemberInfo member) { foreach (object attribute in member.GetCustomAttributes(true)) { if (attribute is IsTestedAttribute) { return true; } } return false; } private static void DumpAttributes(MemberInfo member) { Console.WriteLine("属性 : " + member.Name); foreach (object attribute in member.GetCustomAttributes(true)) { Console.WriteLine(attribute); } } public static void Main() { // 为Account类显示属性 DumpAttributes(typeof(Account)); // 显示测试成员名单 foreach (MethodInfo method in (typeof(Account)).GetMethods()) { if (IsMemberTested(method)) { Console.WriteLine("会员{0}已经测试!", method.Name); } else { Console.WriteLine("会员{0}还没测试!", method.Name); } } Console.WriteLine(); // 显示Order类的属性 DumpAttributes(typeof(Order)); // 关于该命令类的方法显示属性 foreach (MethodInfo method in (typeof(Order)).GetMethods()) { if (IsMemberTested(method)) { Console.WriteLine("会员{0}已经测试!", method.Name); } else { Console.WriteLine("会员{0}还没测试!", method.Name); } } Console.WriteLine(); Console.Read(); } }
按下F5开始调试,运行界面如下: