使用描述将字符串转换为枚举

11
我目前正在实现一个基于这个建议的字符串和枚举的关联。也就是说,我有一个与每个枚举元素相关联的Description属性。在该页面上还有一个函数,它根据给定的枚举返回描述的字符串。现在我想要实现的是反向函数,也就是说,给定一个输入字符串,如果存在相应描述的枚举,则查找该枚举并返回 null 否则。
我尝试过使用(T) Enum.Parse(typeof(T), "teststring"),但它会抛出异常。

可能是重复的问题:您可以访问特定枚举值的长描述吗? - NotMe
5个回答

18

你需要编写自己的反转方法。股票Parse()方法显然不知道你的描述属性。

类似这样的代码应该可以工作:

public static T GetEnumValueFromDescription<T>(string description)
{
    MemberInfo[] fis = typeof(T).GetFields();

    foreach (var fi in fis)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attributes != null && attributes.Length > 0 && attributes[0].Description == description)
            return (T)Enum.Parse(typeof(T), fi.Name);
    }

    throw new Exception("Not found");
}

你需要找到一个更好的解决方案,而不是在枚举值未找到时抛出异常。 :)

1
很棒的解决方案!我选择了默认的枚举解析而不是抛出异常。这样就可以覆盖在枚举中可能没有为每个选项设置描述属性的情况。 - norepro
这对我没用。不得不做一些更改。下面的代码来自于for each循环内部。希望能帮到别人 :) var attributes = fi.CustomAttributes; if (attributes != null && attributes.Count() > 0 && (string)attributes.First().NamedArguments.First().TypedValue.Value == description) return (int)Enum.Parse(typeof(TEnum), fi.Name); - ccoutinho

3
您也可以使用Humanizer来实现这一功能。获取描述的方法如下:
EAssemblyUnit.eUCAL1.Humanize();

如果你想从描述中获取枚举值,你可以编写以下代码:

"UCAL1".DehumanizeTo<EAssemblyUnit>();

免责声明:我是Humanizer的创建者。

2
static string GetEnumDescription<T>(T value) {
    FieldInfo fi = value.GetType().GetField(value.ToString());

    DescriptionAttribute[] attributes =
        (DescriptionAttribute[])fi.GetCustomAttributes(
            typeof(DescriptionAttribute),
            false
    );

    if (attributes != null &&
        attributes.Length > 0) {
        return attributes[0].Description;
    }
    else {
        return value.ToString();
    }
}

static T ParseDescriptionToEnum<T>(string description) {
    Array array = Enum.GetValues(typeof(T));
    var list = new List<T>(array.Length);
    for(int i = 0; i < array.Length; i++) {
        list.Add((T)array.GetValue(i));
    }

    var dict = list.Select(v => new { 
                   Value = v,
                   Description = GetEnumDescription(v) }
               )
                   .ToDictionary(x => x.Description, x => x.Value);
    return dict[description];
}

我没有进行错误检查。请注意,字典不需要在每次调用方法时都创建,但是我太懒了,没有修复它。

用法:

enum SomeEnum {
    [Description("First Value")]
    FirstValue,
    SecondValue
}

SomeEnum value = ParseDescriptionToEnum<SomeEnum>("First Value");

一个通过的测试:

[Fact]
public void Can_parse_a_value_with_a_description_to_an_enum() {
    string description = "First Value";
    SomeEnum value = ParseDescriptionToEnum<SomeEnum>(description);
    Assert.Equal(SomeEnum.FirstValue, value);
}

[Fact]
public void Can_parse_a_value_without_a_description_to_an_enum() {
    string description = "SecondValue";
    SomeEnum value = ParseDescriptionToEnum<SomeEnum>(description);
    Assert.Equal(SomeEnum.SecondValue, value);
}

1
我本想给Anna的答案点赞,但是由于我的声望不够,无法这么做。基于她的回答,我想出了一个双向解决方案。在ParseEnum方法中提供defaultValue可以覆盖同一枚举类型在不同情况下具有不同默认值的情况。
    public static string GetDescription<T>(this object enumerationValue) where T : struct
    {
        // throw an exception if enumerationValue is not an Enum
        Type type = enumerationValue.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
        }

        //Tries to find a DescriptionAttribute for a potential friendly name for the enum
        MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
        if (memberInfo != null && memberInfo.Length > 0)
        {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes != null && attributes.Length > 0)
            {
                //Pull out the description value
                return attributes[0].Description;
            }
        }

        //In case we have no description attribute, we'll just return the ToString of the enum
        return enumerationValue.ToString();
    }

    public static T ParseEnum<T>(this string stringValue, T defaultValue)
    {
        // throw an exception if T is not an Enum
        Type type = typeof(T);
        if (!type.IsEnum)
        {
            throw new ArgumentException("T must be of Enum type", "T");
        }

        //Tries to find a DescriptionAttribute for a potential friendly name for the enum
        MemberInfo[] fields = type.GetFields();

        foreach (var field in fields)
        {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes != null && attributes.Length > 0 && attributes[0].Description == stringValue)
            {
                return (T)Enum.Parse(typeof(T), field.Name);
            }
        }

        //In case we couldn't find a matching description attribute, we'll just return the defaultValue that we provided
        return defaultValue;            
    }

0

这个答案给出了如何检索给定类型的属性。您可以使用类似的方法将给定字符串与枚举的描述属性进行比较。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接