公共语言运行库允许添加类似关键字的描述性声明(称为属性 (Attribute))来批注编程元素,如类型、字段、方法和属性 (Property)。
为运行库编译代码时,该代码被转换为 Microsoft 中间语言 (MSIL),并同编译器生成的元数据一起被放到可移植可执行 (PE) 文件的内部。属性使您得以向元数据中放置额外的描述性信息,并可使用运行库反射服务提取该信息。当声明从 System.Attribute
派生的特殊类的实例时,编译器会创建属性。
编写自定义属性
自定义属性实质上是直接或间接地从 System.Attribute
派生的传统类。与传统类一样,自定义属性也包含存储和检索数据的方法。正确设计自定义属性类的主要步骤如下:
- 应用 AttributeUsageAttribute
自定义属性声明以 AttributeUsageAttribute 开始,而该属性定义属性类的一些主要属性。例如,可指定属性是否可被其他类继承,或指定属性可应用到哪些元素。e.g. [AttributeUsage(AttributeTargets.All, Inherited = false,
AllowMultiple = true)]。System.AttributeUsageAttribute 包含三个对创建自定义属性具有重要意义的成员:AttributeTargets、Inherited 和 AllowMultiple。
AttributeTargets 成员:
AttributeTargets.All,指示该属性可以应用到所有程序元素;
AttributeTargets.Class,指示属性只可以应用于某个类;
AttributeTargets.Method,指示属性只可以应用于某个方法;
所有程序元素都可通过这种方式由自定义属性标记,以便对其进行描述。
继承属性:
Inherited
属性指示属性是否可由从该属性应用到的类派生的类继承。该属性采用
true(默认值)或 false 标志。
AllowMultiple 属性
AllowMultiple
属性指示元素中是否可存在属性的多个实例。如果设置为 true,则允许存在多个实例;如果设置为 false(默认值),则只允许存在一个实例。
如果 AllowMultiple 属性和 Inherited 属性都设置为 true,则从另一个类继承的类可以继承一个属性,并在同一子类中应用同一属性的另一个实例。如果 AllowMultiple 设置为 false,则父类中的所有属性值将被子类中同一属性的新实例覆盖。
声明属性类,注意一下几点:
a) 属性类必须声明为公共类。
b) 按照约定,属性类的名称以单词 Attribute 结尾。虽然并不要求这样,但出于可读性目的,建议采用此约定。应用属性时,可以选择是否包含 Attribute 一词。
c) 所有属性类都必须直接或间接地从 System.Attribute 继承。
检索自定义属性信息
检索自定义属性的过程很简单。首先,声明要检索的属性实例。然后,使用 Attribute.GetCustomAttribute
方法将新属性初始化为要检索的属性值。初始化新属性后,只需使用其属性获取值即可。本主题描述如何检索已加载到执行上下文中的代码的属性。若要检索已加载到只反射上下文中的代码的属性,必须使用 CustomAttributeData 类。
- 检索属性的一个实例
- 检索应用到同一范围的属性的多个实例,如果类级别上只应用属性的一个实例,则该代码将运行良好。但是,如果在同一类级别上应用属性的多个实例,则
GetCustomAttribute 方法不检索所有信息。如果同一个属性的多个实例应用到相同的范围,可使用 Attribute.GetCustomAttributes
将属性的所有实例放到一个数组中。
3. 检索应用到不同范围的属性的多个实例,GetCustomAttributes 方法和
GetCustomAttribute 方法不搜索整个类和返回该类中某个属性的所有实例。相反, 它们一次只搜索一个指定方法或成员。如果将具有同一属性的某个类应用到每个成员,并要检索应用到这些成员的所有属性值,则必须向 GetCustomAttributes 和
GetCustomAttribute 分别提供每个方法或成员。
示例:
属性类定义
代码
1 [AttributeUsage(AttributeTargets.All)]
2 public class DeveloperAttribute : System.Attribute
3 {
4 private string name;
5 private string level;
6 private bool reviewed;
7
8 public DeveloperAttribute(string name, string level)
9 {
10 this.name = name;
11 this.level = level;
12 this.reviewed = false;
13 }
14
15 //Define Name property.
16 //This is a read-only attribute.
17
18 public virtual string Name
19 {
20 get { return name; }
21 }
22
23 //Define Level property.
24 //This is a read-only attribute.
25
26 public virtual string Level
27 {
28 get { return level; }
29 }
30
31 //Define Reviewed property.
32 //This is a read/write attribute.
33
34 public virtual bool Reviewed
35 {
36 get { return reviewed; }
37 set { reviewed = value; }
38 }
39 }
属性类应用
代码
1 [Developer("Joan Smith", "42", Reviewed = true)]
2 public class DeveloperApp
3 {
4 public static DeveloperApp Instance
5 {
6 get
7 {
8 return new DeveloperApp();
9 }
10 }
11
12 /// <summary>
13 /// 检索属性的一个实例
14 /// </summary>
15 public void GetAttribute()
16 {
17 DeveloperAttribute myAttribute = System.Attribute.GetCustomAttribute(this.GetType(), typeof(DeveloperAttribute)) as DeveloperAttribute;
18
19 if (myAttribute == null)
20 {
21 Console.WriteLine("The attribute is not found");
22 }
23 else
24 {
25 Console.WriteLine("The Name Attribute is: {0}.", myAttribute.Name);
26 Console.WriteLine("The Level Attribute is: {0}.", myAttribute.Level);
27 Console.WriteLine("The Reviewed Attribute is: {0}.", myAttribute.Reviewed);
28 }
29 }
30
31 /// <summary>
32 /// 检索应用到同一范围的属性的多个实例
33 /// </summary>
34 /// <param name="toClass">class-level</param>
35 public void GetAttribute(string class_level)
36 {
37 DeveloperAttribute[] myAttributes = System.Attribute.GetCustomAttributes(this.GetType(), typeof(DeveloperAttribute)) as DeveloperAttribute[];
38
39 if (myAttributes == null)
40 {
41 Console.WriteLine("The attribute is not found");
42 }
43 else
44 {
45 foreach (DeveloperAttribute myAttribute in myAttributes)
46 {
47 Console.WriteLine("The Name Attribute is: {0}.", myAttribute.Name);
48 Console.WriteLine("The Level Attribute is: {0}.", myAttribute.Level);
49 Console.WriteLine("The Reviewed Attribute is: {0}.", myAttribute.Reviewed);
50 }
51 }
52 }
53
54 public void GetAttribute(string class_level, string method_level)
55 {
56 GetAttribute();//class_level
57
58 DeveloperAttribute developerAtt;
59 MemberInfo[] member = this.GetType().GetMethods();
60
61 foreach (MemberInfo obj in member)
62 {
63 developerAtt = System.Attribute.GetCustomAttribute(obj, typeof(DeveloperAttribute)) as DeveloperAttribute;
64 if (null == developerAtt)
65 {
66 Console.WriteLine("No attribute in member function {0}.\n", obj.ToString());
67 }
68 else
69 {
70 Console.WriteLine("The Name Attribute for the {0} member is: {1}.", obj.ToString(), developerAtt.Name);
71 Console.WriteLine("The Level Attribute for the {0} member is: {1}.", obj.ToString(), developerAtt.Level);
72 Console.WriteLine("The Reviewed Attribute for the {0} member is: {1}.\n",obj.ToString(), developerAtt.Reviewed);
73 }
74 }
75 }
76 }
自定义属性检索的三个方法比较
- IsDefined:当至少有一个指定的属性类(实例)与目标相关联时,返回true。此方法效率最高,因为它不构造(反序列化)属性类的实例。
- GetCustomeAttributes:返回应用于目标的属性类(实例)数组。对于AllowMultiple设为true的属性,一般应使用此方法。
- GetCustomeAttribute:返回应用于目标的属性类(实例)。若目标没有应用属性类,则返回null,对于应用了多个AllowMultiple=false的属性,会出现System.Reflection.AmbiguousMatchException 异常。