c# - 扩展枚举,矫枉过正?

标签 c# enums attributes extension-methods

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

我有很多像下面这样的应用了自定义属性的枚举。

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,
}

通过扩展方法访问属性:

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();
}

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

    StringBuilder sb = new StringBuilder();
    sb.Append(car.RoofStyle.GetStringValue());
    sb.Append(car.TireSize.GetStringValue());
    sb.Append(car.Model.GetStringValue());
    ...

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

   car.RoofStyle = Enums.GetCode<RoofStyle>(EDIString.Substring(4, 3))

定义为:

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);
        }
} 

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

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

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

最佳答案

您用来序列化的部分没问题。反序列化部分写得很别扭。主要问题是您使用的是 ToUpper()比较容易损坏的字符串(想想全局化)。这种比较应该用 string.Compare 来完成。相反,或者 string.Equals overload这需要 StringComparison .

另一件事是在反序列化过程中一次又一次地执行这些查找会非常慢。如果您正在序列化大量数据,这实际上可能会非常明显。在这种情况下,您需要从 StringValue 构建 map 到枚举本身 - 将其放入静态 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(...);
    }
}

它不是那么“通用”,但效率更高。如果您不喜欢字符串值的重复,则将代码提取为单独类中的常量。

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

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

附言次要细节,但您可能要考虑使用 Attribute.GetCustomAttribute而不是 MemberInfo.GetCustomAttributes如果您只希望有一个属性。当您只需要一项时,没有理由摆弄所有数组。

关于c# - 扩展枚举,矫枉过正?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3015893/

相关文章:

c# - .NET winforms 应用程序在不使用 ClickOnce 的情况下更新自身的最佳方式是什么?

c++ - 口袋妖怪类型的枚举常量

javascript - 如何使用 JQuery 根据文本值将不同的属性分配给同一类的多个元素

JavaScript:在 new Option() 之后添加额外属性

c# - 在 C# 中扩展 TcpClient 类

c# - 从继承另一个的类中获取变量

c# - Kernel32.dll 中的 CreateFile 返回无效句柄

c# - 在 C# 中扩展枚举

java - 将枚举值与类字段匹配

html - 如何在输入标签中设置 value 属性的样式?