自定义枚举解析



我有一个enum如下:

public enum MyEnum {  One,  Two,  Three}

我想把一些字符串压缩到上面的enum,例如,下面的字符串将被解析为MyEnum。二:

"Two", "TWO", "Second", "2"

我知道我可以维护一个映射函数来完成这个工作。然而,我只是想找到一个更好的方法,例如,覆盖Enum。解析函数,或者类似的东西。我试过使用IConvertable,但似乎不可能。任何想法?

[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class NameAttribute : Attribute
{
    public readonly string[] Names;
    public NameAttribute(string name)
    {
        if (name == null)
        {
            throw new ArgumentNullException();
        }
        Names = new[] { name };
    }
    public NameAttribute(params string[] names)
    {
        if (names == null || names.Any(x => x == null))
        {
            throw new ArgumentNullException();
        }
        Names = names;
    }
}
public static class ParseEnum
{
    public static TEnum Parse<TEnum>(string value) where TEnum : struct
    {
        return ParseEnumImpl<TEnum>.Values[value];
    }
    public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
    {
        return ParseEnumImpl<TEnum>.Values.TryGetValue(value, out result);
    }
    private static class ParseEnumImpl<TEnum> where TEnum : struct
    {
        public static readonly Dictionary<string, TEnum> Values = new Dictionary<string,TEnum>();
        static ParseEnumImpl()
        {
            var nameAttributes = typeof(TEnum)
                .GetFields()
                .Select(x => new 
                { 
                    Value = x, 
                    Names = x.GetCustomAttributes(typeof(NameAttribute), false)
                        .Cast<NameAttribute>() 
                });
            var degrouped = nameAttributes.SelectMany(
                x => x.Names.SelectMany(y => y.Names), 
                (x, y) => new { Value = x.Value, Name = y });
            Values = degrouped.ToDictionary(
                x => x.Name, 
                x => (TEnum)x.Value.GetValue(null));
        }
    }
}

那么您可以(注意[Name],多个[Name]或单个[Name]具有多个名称的双重语法):

public enum TestEnum
{
    [Name("1")]
    [Name("Foo")]
    [Name("F")]
    [Name("XF", "YF")]
    Foo = 1,
    [Name("2")]
    [Name("Bar")]
    [Name("B")]
    [Name("XB", "YB")]
    Bar = 2
}

TestEnum r1 = ParseEnum.Parse<TestEnum>("XF");
TestEnum r2;
bool r3 = ParseEnum.TryParse<TestEnum>("YB", out r2);

注意使用内部类(ParseEnumImpl<TEnum>)来缓存TEnum"名称"。

最好的方法是用映射存储Dictionary:

static Dictionary<string, string> _mappings = new Dictionary<string, string>
{
    { "Two", "Two" },
    { "Second", "Two" },
    { "2", "Two" }
};

然后调用Enum.Parse(Type, String, Boolean)的大小写不敏感版本:

String str = "2";
MyEnum number = (MyEnum)Enum.Parse(typeof(MyEnum), _mappings[str], true);

通常,我更喜欢简单的解决方案,因为它们比"覆盖Enum.Parse函数或类似的东西"更容易理解。

但是我们可以通过使用Dictionary<string, MyEnum>:

来做得更简单
static Dictionary<string, MyEnum> _mappings = new Dictionary<string, MyEnum>
{
    { "Two", MyEnum.Two },
    { "Second", MyEnum.Two },
    { "2", MyEnum.Two }
};

现在获取enum:

MyEnum myEnumb = _mappings[str];
后一种方法也提高了性能,因为我们避免了Enum.Parse调用。

您正在尝试解析两种不同的情况:

  1. 输入包含枚举的名称
  2. 输入包含枚举的值

如果输入中只有这两种情况,则可以简单地使用Enum。TryParse方法(String, Boolean, TEnum)尝试以不区分大小写的方式解析文本:

MyEnum output;
if (Enum.TryParse(input,true,out output))
{
    // Process succesful value
}

c# 7+更新:变量现在可以在同一行声明:

if (Enum.TryParse(input,true,out var output))
{
    // Process succesful value
}

从文档示例中可以看到,TryParse可以处理文本和数字字符串输入。

对于解析Second,除了在编码器的头脑中,该文本与enum没有关系。在这种情况下,你真的需要创建一个映射,并把它放在的某个地方——一个字典,一个自定义属性等等。

实际上,如果数据来自外部文件,这是ETL问题,而不是解析问题。在这种情况下,典型的解决方案是创建查找表,将输入映射到可识别的输出,并在解析

之前用查找值替换输入。

可以使用nuget包枚举。. NET对Description属性进行反向查找。

注意这里有一个带ignoreCase布尔值的重载。

public enum PhoneCode
{
    [DescriptionAttribute("991")]
    Emergency,
    [DescriptionAttribute("411")]
    Info,
}
PhoneCode code;
EnumsNET.Enums.TryParse("991", out code, EnumsNET.EnumFormat.Description)

虽然我喜欢这些属性,但我自己使用了映射,相反,我像这样扩展了string基本类型:

public enum Rating
{
    High,
    Medium,
    Low,
    Other
};

然后我有一个static类我的扩展…其中包含以下代码:

public static Dictionary<string, Rating> RatingsMap = new Dictionary<string, Rating>()
{
    {"Highly recommended", Rating.High},
    {"Ok", Rating.Medium},
    {"Liked", Rating.Medium},
    {"Thumbs down", Rating.Low}
};
public static Rating ToRating(this string me)
{
    Rating retval = Rating.Other;
    if (EXTENSIONS.RatingsMap.ContainsKey(me))
        retval = EXTENSIONS.RatingsMap[me];
    return retval;
}

遵循@xanatos解决方案。要获取EnumValue,您可以执行以下操作:

public static string GetStringValue(Enum value)
{
  string output = null;
  Type type = value.GetType();
  System.Reflection.FieldInfo fi = type.GetField(value.ToString());
  NameAttribute[] attrs = fi.GetCustomAttributes(typeof(NameAttribute), false) as NameAttribute[];
  if (attrs.Length > 0)
  {
     output = attrs[0].Name[0];
  }
  return output;
}

相关内容

  • 没有找到相关文章

最新更新