扩展枚举,矫枉过正? [英] Extending Enums, Overkill?

查看:132
本文介绍了扩展枚举,矫枉过正?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个需要被序列化到EDI格式的对象。在这个例子中,我们会说这是一辆汽车。一辆汽车可能不是B / C选项随时间变化最好的例子,但对于真正的对象枚举永远不会改变。

I have an object that needs to be serialized to an EDI format. For this example we'll say it's a car. A car might not be the best example b/c options change over time, but for the real object the Enums will never change.

我有很多喜欢的枚举应用了自定义属性如下。

I have many Enums like the following with custom attributes applied.

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

属性可通过扩展方法来访问:

The Attributes are accessed via Extension methods:

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

有是序列化基础上的StringValue自定义EDI串行属性,像这样:

There is a custom EDI serializer that serializes based on the StringValue attributes like so:

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

有是可以从得到的StringValue枚举值反序列化的另一种方法:

There is another method that can get Enum Value from StringValue for Deserialization:

   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()是用来显示用户友好的文本。

And Finally, for the UI, the GetDisplayText() is used to show the user friendly text.

你觉得呢?矫枉过正?有没有更好的办法?或戈尔迪锁(正好)?

What do you think? Overkill? Is there a better way? or Goldie Locks (just right)?

只是要得到反馈之前,我G642.44各级它变成我个人的框架久治不愈。谢谢。

Just want to get feedback before I intergrate it into my personal framework permanently. Thanks.

推荐答案

您正在使用序列化的部分是罚款。反序列化部分是笨拙写入。主要的问题是,你正在使用 ToUpper的()来比较字符串,这是容易折断(认为全球化)。这种比较应该与的String.Compare 来完成,而不是,或的 string.Equals超载,需要一个 StringComparison

The part you're using to serialize is fine. The deserialization part is awkwardly written. The main problem is that you're using ToUpper() to compare strings, which is easily broken (think globalization). Such comparisons should be done with string.Compare instead, or the string.Equals overload that takes a StringComparison.

另一件事是,反序列化期间一再执行这些查找是要pretty缓慢。如果你序列化大量的数据,这实际上可能是相当明显。在这种情况下,你要建立从的StringValue 地图来枚举本身 - 它扔进一个静态词典&LT;字符串,RoofStyle&GT; ,并用它作为来回查找。换句话说:

The other thing is that performing these lookups again and again during deserialization is going to pretty slow. If you're serializing a lot of data, this could actually be quite noticeable. In that case, you'd want to build a map from the StringValue to the enum itself - throw it into a static Dictionary<string, RoofStyle> and use it as a lookup for the round-trip. In other words:

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

这并不是因为一般的,但它的方式更有效。如果你不喜欢的字符串值的重复然后提取codeS为常量在一个单独的类。

It's not as "generic" but it's way more efficient. If you don't like the duplication of string values then extract the codes as constants in a separate class.

如果你真的需要它是完全通用的,任何工作枚举,你总是可以延迟加载值的字典(使用你写的扩展方法产生的话)的第一次转换完成。这很简单,要做到这一点:

If you really need it to be totally generic and work for any enum, you can always lazy-load the dictionary of values (generate it using the extension methods you've written) the first time a conversion is done. It's very simple to do that:

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

P.S。小细节,但你可能要考虑使用 Attribute.GetCustomAttribute 而不是 MemberInfo.GetCustomAttributes ,如果你只希望那里是一个属性。没有理由对所有阵列摆弄的时候,你只需要一个项目。

P.S. Minor detail but you might want to consider using Attribute.GetCustomAttribute instead of MemberInfo.GetCustomAttributes if you only expect there to be one attribute. There's no reason for all the array fiddling when you only need one item.

这篇关于扩展枚举,矫枉过正?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆