引言:
昨天在项目中有一个需求,就是把枚举转为一个集合,再把转换得到的集合绑定到一个DropDownList上。我开始觉得很简单,因为使用下面的代码很容易解决:
public static List<string> ConvertEnumToList()
{
var values = Enum.GetNames(typeof(StatisticType));
return values.ToList();
}
如上面的代码所示,StatisticType是一个枚举, 定义如下:
public enum StatisticType
{
Input,
Output,
Spend
}
通过调用ConvertEnumToList()得到的集合类可以轻松的绑定到DropDownList上,很明显这个问题就解决了。 但是这个需求中要求,把枚举StatisticType中的
成员Input, Output, Spend分别以”Input Bytes”, “Output Bytes”, “Time Spend” 现在到UI上。 那么ConvertEnumToList方法就不能实现想要的效果了
,好吧,那么就改吧。
我当时的思路是,用一个switch语句进行实现,如下面代码:
public static List<String> ConvertEnumToList()
{
var values = Enum.GetValues(typeof (StatisticType));
List<String> list = new List<string>();
foreach (StatisticType value in values)
{
string displayName = string.Empty;
switch (value)
{
case StatisticType.Input:
displayName = "Input Bytes";
break;
case StatisticType.Output:
displayName = "Output Bytes";
break;
case StatisticType.Spend:
displayName = "Time Spend";
break;
}
list.Add(displayName);
}
return list;
}
好,这下能达到要求了吧,但是仔细又一想,如果StatisticType枚举中有100个成员甚至更多,那么switch不是要100个case?而且一不小心漏掉了一个,修改bugs时可能会很痛苦。 为了避免这些问题,我的解决办法是使用特性。
详细解释:
1. 引言中讲到了枚举绑定到DropDownlist的问题,当前的解决方法是使用switch语句进行解决,它的弊端是降低了程序的扩展性和可维护性。
为了减少这些问题,我使用了特性来解决,直接上代码,之后再分析, 首先修改之前的StatisticType枚举:
public enum StatisticType
{
[Description("Input Bytes")]
Input,
[Description("Output Bytes")]
Output,
[Description("Time Spend")]
Spend
}
改动很少,只是在StatisticType每个成员上附加了一个DescriptionAttribute 特性,并使用需要在UI上显示的字符串作用参数,去调用构造函数。
这些数据都会被写入到Metadata中。
2. 下一步定义一个扩展方法,来获取StatisticType所有成员的DescriptionAttribute特性,并解析出在UI上显示的字符串,代码如下:
public static class GeneralClassExtension
{
public static TExpected GetAttributePropertyValueFromEnum<T, TExpected>(this Enum enumeration, Func<T, TExpected> expression) where T : Attribute
{
var memberInfo = enumeration.GetType().GetMember(enumeration.ToString())[0];
if (!memberInfo.IsDefined(typeof(T), false))
return default(TExpected);
T attribute = memberInfo.GetCustomAttributes(typeof(T), false).Cast<T>().SingleOrDefault();
return expression(attribute);
}
}
这段代码,我在看的时候遇到了一个疑惑,主要是在理解 var memberInfo = enumeration.GetType().GetMember(enumeration.ToString())[0] 这段代码上,一个枚举值首先通过
GetType()得到它的类型的Type对象,再使用GeMember获得这个这个枚举值本身的反射对象。 很绕口,那到底枚举值枚举类型是个神马关系啊? 还好我们有IL反编译工具, 如下图:
由上图很明显得出结论,枚举StatisticType 中的枚举值 Input, Output, Spend 其实就是类型为StatisticType的static字段。之后的代码,就是利用反射获取UI上显示的字符串。
那么现在ConvertEnumToList方法就应该改为:
public static List<String> ConvertEnumToList()
{
var values = Enum.GetValues(typeof(StatisticType));
List<String> list = new List<string>();
foreach (StatisticType value in values)
{
var displayName = value.GetAttributePropertyValueFromEnum<DescriptionAttribute, string>(m => m.Description);
list.Add(displayName);
}
return list;
}
好的,这样就实现了利用Attribute将枚举类型转化为具体的集合。如果你修改添加更多的枚举值,你只需要修改该枚举类型,而不用去修改其他的代码。
更不用些很长得switch去转换。