C#自定义Attribute值的获取是开发中会经常用到的,一般我们的做法也就是用反射进行获取的,代码也不是很复杂。
1、首先有如下自定义的Attribute
[AttributeUsage(AttributeTargets.All)]
public sealed class NameAttribute : Attribute
{
private readonly string _name;
public string Name
{
get { return _name; }
}
public NameAttribute(string name)
{
_name = name;
}
}
2、定义一个使用NameAttribute的类
[Name("dept")]
public class CustomAttributes
{
[Name("Deptment Name")]
public string Name { get; set; }
[Name("Deptment Address")]
public string Address;
}
3、获取CustomAttributes类上的"dept"也就很简单了
private static string GetName()
{
var type = typeof(CustomAttributes);
var attribute = type.GetCustomAttributes(typeof(NameAttribute), false).FirstOrDefault();
if (attribute == null)
{
return null;
}
return ((NameAttribute)attribute).Name;
}
以上代码就可以简单的获取,类上的Attribute的值了,但是需求往往不是这么简单的,不仅要获取类头部Attribute上的值,还要获取字段Address头部Attribute上的值。有的同学可能就觉得这还不简单呀,直接上代码
private static string GetAddress()
{
var type = typeof (CustomAttributes);
var fieldInfo = type.GetField("Address");
if (fieldInfo == null)
{
return null;
}
var attribute = fieldInfo.GetCustomAttributes(typeof(NameAttribute), false).FirstOrDefault();
if (attribute == null)
{
return null;
}
return ((NameAttribute) attribute).Name;
}
上面代码就是获取Address字段头部上的Attribute值了。虽然我们是获取到了我们想要的,但是我们发现这样做是不是太累了,如果又扩展一个自定义的Attribute,或者又在一个新的属性或字段上标上Attribute时,我们又要写一段代码来实现我想要的,这些严重代码违反了DRY的设计原则。我们知道获取Attribute是通过反射来取的,Attribute那个值又是不变的,这样就没必要每次都要进行反射来获取了。基于以上两点代码进行了如下的优化,优化后的代码如下:
public static class CustomAttributeHelper
{
/// <summary>
/// Cache Data
/// </summary>
private static readonly Dictionary<string, string> Cache = new Dictionary<string, string>();
/// <summary>
/// 获取CustomAttribute Value
/// </summary>
/// <typeparam name="T">Attribute的子类型</typeparam>
/// <param name="sourceType">头部标有CustomAttribute类的类型</param>
/// <param name="attributeValueAction">取Attribute具体哪个属性值的匿名函数</param>
/// <returns>返回Attribute的值,没有则返回null</returns>
public static string GetCustomAttributeValue<T>(this Type sourceType, Func<T, string> attributeValueAction) where T : Attribute
{
return GetAttributeValue(sourceType, attributeValueAction, null);
}
/// <summary>
/// 获取CustomAttribute Value
/// </summary>
/// <typeparam name="T">Attribute的子类型</typeparam>
/// <param name="sourceType">头部标有CustomAttribute类的类型</param>
/// <param name="attributeValueAction">取Attribute具体哪个属性值的匿名函数</param>
/// <param name="name">field name或property name</param>
/// <returns>返回Attribute的值,没有则返回null</returns>
public static string GetCustomAttributeValue<T>(this Type sourceType, Func<T, string> attributeValueAction,
string name) where T : Attribute
{
return GetAttributeValue(sourceType, attributeValueAction, name);
}
private static string GetAttributeValue<T>(Type sourceType, Func<T, string> attributeValueAction,
string name) where T : Attribute
{
var key = BuildKey(sourceType, name);
if (!Cache.ContainsKey(key))
{
CacheAttributeValue(sourceType, attributeValueAction, name);
}
return Cache[key];
}
/// <summary>
/// 缓存Attribute Value
/// </summary>
private static void CacheAttributeValue<T>(Type type,
Func<T, string> attributeValueAction, string name)
{
var key = BuildKey(type, name);
var value = GetValue(type, attributeValueAction, name);
lock (key + "_attributeValueLockKey")
{
if (!Cache.ContainsKey(key))
{
Cache[key] = value;
}
}
}
private static string GetValue<T>(Type type,
Func<T, string> attributeValueAction, string name)
{
object attribute = null;
if (string.IsNullOrEmpty(name))
{
attribute =
type.GetCustomAttributes(typeof (T), false).FirstOrDefault();
}
else
{
var propertyInfo = type.GetProperty(name);
if (propertyInfo != null)
{
attribute =
propertyInfo.GetCustomAttributes(typeof (T), false).FirstOrDefault();
}
var fieldInfo = type.GetField(name);
if (fieldInfo != null)
{
attribute = fieldInfo.GetCustomAttributes(typeof (T), false).FirstOrDefault();
}
}
return attribute == null ? null : attributeValueAction((T) attribute);
}
/// <summary>
/// 缓存Collection Name Key
/// </summary>
private static string BuildKey(Type type, string name)
{
if (string.IsNullOrEmpty(name))
{
return type.FullName;
}
return type.FullName + "." + name;
}
}
以上优化后的代码:
把不同的代码用泛型T,Fun<T,stirng>来处理来减少重复的代码;
把取过的Attribute值存到一个Dictionary中,下次再来取时,如果有则直接返回Dictionary中的值,如果没有才通过反射来取相应的Attribute值,这样大大的提高效率;
调用方法也更加的简单了,代码如下:
var cName=typeof(CustomAttributes).GetCustomAttributeValue<NameAttribute>(x => x.Name);
var fName = typeof (CustomAttributes).GetCustomAttributeValue<NameAttribute>(x => x.Name, "Address");
有没有, 是不是很简单,而且调用方式对缓存是完全透明的!