扩展枚举,过度杀毒? [英] Extending Enums, Overkill?

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

问题描述

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



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

  public enum RoofStyle 
{
[DisplayText(Glass Top)]
[StringValue(GTR)]
Glass,
[DisplayText(Convertible Soft顶部)]
[StringValue(CST)]
ConvertibleSoft,
[DisplayText(Hard Top)]
[StringValue(HT)]
HardTop,
[DisplayText(Targa Top)]
[StringValue(TT)]
Targa,
}

通过扩展方法访问属性:

  public static string GetStringValue(此枚举值)
{
//获取类型
类型type = value.GetType();

//获取此类型的fieldinfo
FieldInfo fieldInfo = type.GetField(value.ToString());

//获取stringvalue属性
StringValueAttribute []; attribs = fieldInfo.GetCustomAttributes(
typeof(StringValueAttribute),false)as StringValueAttribute [];

//如果有匹配,返回第一个。
return attribs.Length> 0? attribs [0] .StringValue:null;
}

public static string GetDisplayText(此枚举值)
{
//获取类型
类型type = value.GetType();

//获取此类型的fieldinfo
FieldInfo fieldInfo = type.GetField(value.ToString());

//获取DisplayText属性
DisplayTextAttribute []的DisplayTextAttribute []; attribs = fieldInfo.GetCustomAttributes(
typeof(DisplayTextAttribute),false)

//如果有匹配,返回第一个。
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获取反序列化的枚举值:

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

定义为:

  public static class Enums 
{
public static T GetCode< T>(string value)
{
foreach(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()+对应于值++);
}
}

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



你觉得怎么样?矫枉过正?有没有更好的办法?或Goldie Locks(正确的)?



只需要获得反馈,然后再将其整合到我的个人框架中。谢谢。

解决方案

您用于序列化的部分是正常的。反序列化部分写得很尴尬。主要的问题是您使用 ToUpper()来比较字符串,这很容易被破坏(思考全球化)。这样的比较应该使用 string.Compare string.Equals overload ,它需要一个 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结果;
if(roofStyles.TryGetValue(code,out result))
return result;
throw new ArgumentException(...);
}
}

它不是通用,而是更有效率。如果你不喜欢字符串值的重复,那么在一个单独的类中提取代码作为常量。



如果你真的需要它是完全通用的,适用于任何枚举,您可以始终懒惰加载值的字典(使用您编写的扩展方法生成它)首次完成转换。这样做很简单:

 静态字典< string,T> CreateEnumLookup< T>()
{
返回Enum.GetValues(typeof(T))。ToDictionary(o =>((Enum)o).GetStringValue(),
o => (至);
}

细节细节,但是如果您只希望能够使用 Attribute.GetCustomAttribute 而不是 MemberInfo.GetCustomAttributes 一个属性。当您只需要一个项目时,所有阵列都没有任何理由。


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

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

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

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

Defined as:

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

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)?

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

解决方案

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.

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

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. 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天全站免登陆