扩展枚举,矫枉过正?

CkH*_*CkH 4 c# extension-methods enums attributes

我有一个需要序列化为EDI格式的对象.对于这个例子,我们会说它是一辆汽车.汽车可能不是b/c选项随时间变化的最佳示例,但对于真实对象,Enums永远不会改变.

我有许多枚举,如下所示,应用了自定义属性.

public enum RoofStyle
{
    [DisplayText("Glass Top")]
    [StringValue("GTR")]
    Glass,
    [DisplayText("Convertible Soft Top")]
    [StringValue("CST")]
    ConvertibleSoft,
    [DisplayText("Hard Top")]
    [StringValue("HT ")]
    HardTop,
    [DisplayText("Targa Top")]
    [StringValue("TT ")]
    Targa,
}
Run Code Online (Sandbox Code Playgroud)

通过扩展方法访问属性:

public static string GetStringValue(this Enum value)
{
    // Get the type
    Type type = value.GetType();

    // Get fieldinfo for this type
    FieldInfo fieldInfo = type.GetField(value.ToString());

    // Get the stringvalue attributes
    StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
        typeof(StringValueAttribute), false) as StringValueAttribute[];

    // Return the first if there was a match.
    return attribs.Length > 0 ? attribs[0].StringValue : null;
}

public static string GetDisplayText(this Enum value)
{
    // Get the type
    Type type = value.GetType();

    // Get fieldinfo for this type
    FieldInfo fieldInfo = type.GetField(value.ToString());

    // Get the DisplayText attributes
    DisplayTextAttribute[] attribs = fieldInfo.GetCustomAttributes(
        typeof(DisplayTextAttribute), false) as DisplayTextAttribute[];

    // Return the first if there was a match.
    return attribs.Length > 0 ? attribs[0].DisplayText : value.ToString();
}
Run Code Online (Sandbox Code Playgroud)

有一个自定义EDI序列化程序,它基于StringValue属性进行序列化,如下所示:

    StringBuilder sb = new StringBuilder();
    sb.Append(car.RoofStyle.GetStringValue());
    sb.Append(car.TireSize.GetStringValue());
    sb.Append(car.Model.GetStringValue());
    ...
Run Code Online (Sandbox Code Playgroud)

还有另一种方法可以从StringValue获取Enum Value以进行反序列化:

   car.RoofStyle = Enums.GetCode<RoofStyle>(EDIString.Substring(4, 3))
Run Code Online (Sandbox Code Playgroud)

定义为:

public static class Enums
    {
        public static T GetCode<T>(string value)
        {
            foreach (object o in System.Enum.GetValues(typeof(T)))
            {
                if (((Enum)o).GetStringValue() == value.ToUpper())
                    return (T)o;
            }
            throw new ArgumentException("No code exists for type " + typeof(T).ToString() + " corresponding to value of " + value);
        }
} 
Run Code Online (Sandbox Code Playgroud)

最后,对于UI,GetDisplayText()它用于显示用户友好的文本.

你怎么看?矫枉过正?有没有更好的办法?还是Goldie Locks(恰到好处)?

只是想在我将其永久地集成到我的个人框架之前获得反馈.谢谢.

Aar*_*ght 7

您用于序列化的部分很好.反序列化部分写得很笨拙.主要的问题是你ToUpper()用来比较字符串,这很容易被打破(想想全球化).这样的比较应该用string.Compare,或者string.Equals重载,需要一个StringComparison.

另一件事是在反序列化期间反复执行这些查找会非常缓慢.如果你要序列化大量数据,这实际上可能非常明显.在这种情况下,您需要构建一个从StringValue枚举到枚举的映射- 将其抛入静态Dictionary<string, RoofStyle>并将其用作往返的查找.换一种说法:

public static class Enums
{
    private static Dictionary<string, RoofStyle> roofStyles =
        new Dictionary<string, RoofStyle>()
    {
        { "GTR", RoofStyle.Glass },
        { "CST", RoofStyle.ConvertibleSoft },
        { "HT ", RoofStyle.HardTop },
        { "TT ", RoofStyle.TargaTop }
    }

    public static RoofStyle GetRoofStyle(string code)
    {
        RoofStyle result;
        if (roofStyles.TryGetValue(code, out result))
            return result;
        throw new ArgumentException(...);
    }
}
Run Code Online (Sandbox Code Playgroud)

它不像"通用",但它的效率更高.如果您不喜欢字符串值的重复,则在单独的类中将代码提取为常量.

如果你真的需要它是完全通用的并适用于任何枚举,你总是可以在第一次转换时懒惰加载值的字典(使用你编写的扩展方法生成它).这样做非常简单:

static Dictionary<string, T> CreateEnumLookup<T>()
{
    return Enum.GetValues(typeof(T)).ToDictionary(o => ((Enum)o).GetStringValue(),
        o => (T)o);
}
Run Code Online (Sandbox Code Playgroud)

PS次要细节,但您可能需要考虑使用Attribute.GetCustomAttribute而不是MemberInfo.GetCustomAttributes如果您只希望有一个属性.当你只需要一个项目时,没有理由让所有阵列摆弄.