C#自定義Attribute值的獲取是開發中會經常用到的,一般我們的做法也就是用反射進行獲取的,代碼也不是很復雜。
1、首先有如下自定義的Attribute
1 [AttributeUsage(AttributeTargets.All)] 2 public sealed class NameAttribute : Attribute 3 { 4 private readonly string _name; 5 6 public string Name 7 { 8 get { return _name; } 9 } 10 11 public NameAttribute(string name) 12 { 13 _name = name; 14 } 15 }
2、定義一個使用NameAttribute的類
[Description("Customer Information")] [Name("customer_info")] public class CustomerInfo { [Name("name")] public string Name { get; set; } [Name("address")] public string Address; }
3、獲取CustomAttributes類上的"dept"也就很簡單了
1 private static string GetName() 2 { 3 var type = typeof(CustomAttributes); 4 5 var attribute = type.GetCustomAttributes(typeof(NameAttribute), false).FirstOrDefault(); 6 7 if (attribute == null) 8 { 9 return null; 10 } 11 12 return ((NameAttribute)attribute).Name; 13 }
以上代碼就可以簡單的獲取,類上的Attribute的值了,但是需求往往不是這么簡單的,不僅要獲取類頭部Attribute上的值,還要獲取字段Address頭部Attribute上的值。有的同學可能就覺得這還不簡單呀,直接上代碼
1 private static string GetAddress() 2 { 3 var type = typeof (CustomAttributes); 4 5 var fieldInfo = type.GetField("Address"); 6 if (fieldInfo == null) 7 { 8 return null; 9 } 10 11 var attribute = fieldInfo.GetCustomAttributes(typeof(NameAttribute), false).FirstOrDefault(); 12 13 if (attribute == null) 14 { 15 return null; 16 } 17 18 return ((NameAttribute) attribute).Name; 19 }
上面代碼就是獲取Address字段頭部上的Attribute值了。雖然我們是獲取到了我們想要的,但是我們發現這樣做是不是太累了,如果又擴展一個自定義的Attribute,或者又在一個新的屬性或字段上標上Attribute時,我們又要寫一段代碼來實現我想要的,這些嚴重代碼違反了DRY的設計原則。我們知道獲取Attribute是通過反射來取的,Attribute那個值又是不變的,這樣就沒必要每次都要進行反射來獲取了。基於以上兩點代碼進行了如下的優化,優化后的代碼如下:
1 using System; 2 using System.Collections.Concurrent; 3 using System.Reflection; 4 5 public static class CustomAttributeExtensions 6 { 7 /// <summary> 8 /// Cache Data 9 /// </summary> 10 private static readonly ConcurrentDictionary<string, object> Cache = new ConcurrentDictionary<string, object>(); 11 12 /// <summary> 13 /// 獲取CustomAttribute Value 14 /// </summary> 15 /// <typeparam name="TAttribute">Attribute的子類型</typeparam> 16 /// <typeparam name="TReturn">TReturn的子類型</typeparam> 17 /// <param name="sourceType">頭部標有CustomAttribute類的類型</param> 18 /// <param name="attributeValueAction">取Attribute具體哪個屬性值的匿名函數</param> 19 /// <returns>返回Attribute的值,沒有則返回null</returns> 20 public static TReturn GetCustomAttributeValue<TAttribute, TReturn>(this Type sourceType, Func<TAttribute, TReturn> attributeValueAction) 21 where TAttribute : Attribute 22 { 23 return _getAttributeValue(sourceType, attributeValueAction, null); 24 } 25 26 /// <summary> 27 /// 獲取CustomAttribute Value 28 /// </summary> 29 /// <typeparam name="TAttribute">Attribute的子類型</typeparam> 30 /// <typeparam name="TReturn">TReturn的子類型</typeparam> 31 /// <param name="sourceType">頭部標有CustomAttribute類的類型</param> 32 /// <param name="attributeValueAction">取Attribute具體哪個屬性值的匿名函數</param> 33 /// <param name="propertyName">field name或property name</param> 34 /// <returns>返回Attribute的值,沒有則返回null</returns> 35 public static TReturn GetCustomAttributeValue<TAttribute, TReturn>(this Type sourceType, Func<TAttribute, TReturn> attributeValueAction, string propertyName) 36 where TAttribute : Attribute 37 { 38 return _getAttributeValue(sourceType, attributeValueAction, propertyName); 39 } 40 41 #region private methods 42 43 private static TReturn _getAttributeValue<TAttribute, TReturn>(Type sourceType, Func<TAttribute, TReturn> attributeFunc, string propertyName) 44 where TAttribute : Attribute 45 { 46 var cacheKey = BuildKey<TAttribute>(sourceType, propertyName); 47 var value = Cache.GetOrAdd(cacheKey, k => GetValue(sourceType, attributeFunc, propertyName)); 48 if (value is TReturn) return (TReturn)Cache[cacheKey]; 49 return default(TReturn); 50 } 51 52 private static string BuildKey<TAttribute>(Type type, string propertyName) where TAttribute : Attribute 53 { 54 var attributeName = typeof(TAttribute).FullName; 55 if (string.IsNullOrEmpty(propertyName)) 56 { 57 return type.FullName + "." + attributeName; 58 } 59 60 return type.FullName + "." + propertyName + "." + attributeName; 61 } 62 63 private static TReturn GetValue<TAttribute, TReturn>(this Type type, Func<TAttribute, TReturn> attributeValueAction, string name) 64 where TAttribute : Attribute 65 { 66 TAttribute attribute = default(TAttribute); 67 if (string.IsNullOrEmpty(name)) 68 { 69 attribute = type.GetCustomAttribute<TAttribute>(false); 70 } 71 else 72 { 73 var propertyInfo = type.GetProperty(name); 74 if (propertyInfo != null) 75 { 76 attribute = propertyInfo.GetCustomAttribute<TAttribute>(false); 77 } 78 else 79 { 80 var fieldInfo = type.GetField(name); 81 if (fieldInfo != null) 82 { 83 attribute = fieldInfo.GetCustomAttribute<TAttribute>(false); 84 } 85 } 86 } 87 88 return attribute == null ? default(TReturn) : attributeValueAction(attribute); 89 } 90 91 #endregion 92 }
優化后的代碼:
把不同的代碼用泛型T,Fun<TAttribute,TReturn>來處理來減少重復的代碼;
把取過的Attribute值存到一個ConcurrentDictionary中,下次再來取時,如果有則直接取ConcurrentDictionary中的值,如果沒有才通過反射來取相應的Attribute值,這樣大大的提高效率;
調用方法也更加的簡單了,代碼如下:
var customerInfoName = typeof(CustomerInfo).GetCustomAttributeValue<NameAttribute, string>(x => x.Name); var customerAddressName = typeof(CustomerInfo).GetCustomAttributeValue<NameAttribute, string>(x => x.Name, "Address"); var customerInfoDesc = typeof(CustomerInfo).GetCustomAttributeValue<DescriptionAttribute, string>(x => x.Description); Console.WriteLine("CustomerInfo Name:" + customerInfoName); Console.WriteLine("customerInfo >Address Name:" + customerAddressName); Console.WriteLine("customerInfo Desc:" + customerInfoDesc);
運行結果:
如果你有什么好的或不好的意見歡迎拍磚!
謝謝大家的建議,己經更新(2019/11/21)
Code:Download