- 了解attribute
Attribute 只是將一些附加信息與某個目標元素關聯起來的方式。
Attribute 是一個類,這個類可以提供一些字段和屬性,不應提供公共方法,事件等。在定義attribute類的構造方法,字段和屬性時,對數據類型有嚴格的要求,一般要求為: Boolean, Char, Byte, Sbyte, Int16, UInt16, Int32, Int64,Single, Double, String, Type, Object, Enum, 可以使用數組,但是並不提倡使用。
-
使用attribute
1 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false)] 2 [Conditional("Specify")] 3 public class DisplayNameAttribute : Attribute 4 { 5 public string PropertyName { get; set; } 6 7 public DisplayNameAttribute() 8 { } 9 10 public DisplayNameAttribute(string propertyName) 11 { 12 this.PropertyName = propertyName; 13 } 14 }
- 檢測attribute
- attribute定義和應用之后還沒有任何意義,只有在運行的時候去檢測,然后根據對應的attribute做一些不同事情才讓attribute變得有意義,那么如何檢測attribute呢?
| 方法名稱 |
說明 |
| IsDefined |
如果至少有一個指定的Attribute派生類的實例與目標關聯,就返回true。這個方法效率很高,因為他不構造(反序列化)attribute類的任何實例 |
| GetCustomAttributes |
返回一個數組,其中每個元素都是應用於目標的指定attribute類的一個實例。 如果不為這個方法指定具體的attribute類,數組中包含的就是已應用的所有attribute的實例,不管他們是什么類。每個實例都使用編譯時指定的參數、字段和屬性來構造(反序列化)。如果目標沒有應用任何attribute類的實例,就返回一個空數組。該方法通常用於已將AllowMultiple 設為true的attribute |
| GetCustomAttribute |
返回應用於目標的指定attribute類的一個實例。實例使用編譯是指定的參數、字段和屬性來構造(反序列化)。如果目標沒有應用任何attribute類的實例,就返回null。如果目標應用了制定attribute的多個實例,拋出異常AmbiguousMatchException. 因此該方法通常用於AllowMultiple=false的attribute |
最后的例子中展示了如何使用這些方法。
2. 上面的方法在檢測時進行了attribute對象的構造(需要反序列化),這樣有可能調用set訪問器和構造方法,可能出現安全風險(未知代碼在AppDomain中運行)。因此可以使用CustomAttributeData類中定的靜態方法進行檢測
1 public override string ToString() 2 { 3 //在子類中對父類中的方法進行重寫,使用不創建attribute對象不執行代碼(反序列化)的方法 4 StringBuilder value = new StringBuilder(); 5 if (!this.GetType().IsClass) 6 return value.ToString(); 7 PropertyInfo[] properties = this.GetType().GetProperties(); 8 9 foreach (var property in properties) 10 { 11 string propertyName = property.Name; 12 IList<CustomAttributeData> list = CustomAttributeData.GetCustomAttributes(property); 13 foreach (var item in list) 14 { 15 if (item.Constructor.DeclaringType == typeof(DisplayNameAttribute)) 16 { 17 Console.Write("This property applies DisplayNameAttribute."); 18 } 19 } 20 } 21 return base.ToString(); 22 }
- 條件attribute
應用attribute時生成元數據,如果想讓attribute在編譯是不生成元數據可以使用條件編譯,下面是例子
1 #define Specify 2 //#define UnSpecify 3 4 namespace Attribute 5 { 6 using System; 7 using System.Collections.Generic; 8 using System.Linq; 9 using System.Text; 10 using System.Reflection; 11 using System.Diagnostics; 12 13 public class Pepople 14 { 15 [DisplayName("姓名")] 16 [TestDemoAttribute] 17 public string Name { get; set; } 18 } 19 20 public class Man : Pepople 21 { 22 [DisplayName("年齡")] 23 public int Age { get; set; } 24 25 public float Height { get; set; } 26 } 27 28 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false)] 29 [Conditional("Specify")] 30 public class DisplayNameAttribute : Attribute 31 { 32 public string PropertyName { get; set; } 33 34 public DisplayNameAttribute() 35 { } 36 37 public DisplayNameAttribute(string propertyName) 38 { 39 this.PropertyName = propertyName; 40 } 41 } 42 43 [Conditional("UnSpecify")] 44 public class TestDemoAttribute : Attribute 45 { } 46 } 47 條件attribute
注意:贅述一句,在C#中進行預定義時,必須在文件在最前面
我們用ildasm查看元數據得知,TestDemoAttribute 沒有被編譯成員數據,而DisplayNameAttribute被編譯成了元數據

- 實例
對上面提到的知識寫了個例子,以便加強知識的理解運用。
1 #define Specify 2 //#define UnSpecify 3 4 namespace Attribute 5 { 6 using System; 7 using System.Collections.Generic; 8 using System.Linq; 9 using System.Text; 10 using System.Reflection; 11 using System.Diagnostics; 12 13 public class Pepople 14 { 15 [DisplayName("姓名")] 16 [TestDemoAttribute] 17 public string Name { get; set; } 18 19 public override string ToString() 20 { 21 StringBuilder value = new StringBuilder(); 22 if (!this.GetType().IsClass) 23 return value.ToString(); 24 PropertyInfo[] properties = this.GetType().GetProperties(); 25 26 foreach (var property in properties) 27 { 28 string propertyName = property.Name; 29 if (property.IsDefined(typeof(DisplayNameAttribute), false)) 30 { 31 DisplayNameAttribute attr = (DisplayNameAttribute)Attribute.GetCustomAttribute(property, typeof(DisplayNameAttribute)); 32 if (attr != null) 33 { 34 propertyName = attr.PropertyName; 35 } 36 } 37 value.Append(propertyName); 38 value.Append("\t"); 39 value.Append(property.GetValue(this, null)); 40 value.Append("\n\r"); 41 } 42 43 return value.ToString(); 44 } 45 } 46 47 public class Man : Pepople 48 { 49 [DisplayName("年齡")] 50 public int Age { get; set; } 51 52 public float Height { get; set; } 53 } 54 55 public static class OutputerClass 56 { 57 public static string StringClass(object obj) 58 { 59 StringBuilder value = new StringBuilder(); 60 if (!obj.GetType().IsClass) 61 return value.ToString(); 62 PropertyInfo[] properties = obj.GetType().GetProperties(); 63 64 foreach (var property in properties) 65 { 66 DisplayNameAttribute attr = (DisplayNameAttribute)Attribute.GetCustomAttribute(property, typeof(DisplayNameAttribute)); 67 if (attr != null) 68 { 69 value.Append(attr.PropertyName); 70 value.Append("\t"); 71 value.Append(property.GetValue(obj, null)); 72 value.Append("\n\r"); 73 } 74 } 75 76 return value.ToString(); 77 } 78 } 79 80 //可以用attributeUsage聲明屬性應用的范圍,可以多選,多選的時候|運算即可 81 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false)] 82 [Conditional("Specify")] 83 public class DisplayNameAttribute : Attribute 84 { 85 public string PropertyName { get; set; } 86 87 public DisplayNameAttribute() 88 { } 89 90 public DisplayNameAttribute(string propertyName) 91 { 92 this.PropertyName = propertyName; 93 } 94 } 95 96 [Conditional("UnSpecify")] 97 public class TestDemoAttribute : Attribute 98 { } 99 }
