自定义枚举解析

jon*_*nes 3 c# enums

我有一个枚举如下:

public enum MyEnum {  One,  Two,  Three}
Run Code Online (Sandbox Code Playgroud)

我想将一些字符串削减到枚举之上,例如,下面的字符串将被解析为 MyEnum.Two:

"Two", "TWO", "Second", "2"
Run Code Online (Sandbox Code Playgroud)

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

xan*_*tos 5

[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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以(注意[Name], multiple[Name]或 single [Name]with multiple names的双重语法):

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
}
Run Code Online (Sandbox Code Playgroud)

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

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