开发者

String to Enum with Description

I am currently implementing an associacion of strings and enums based on this suggestion. That being, I have a Description attribute associated with every e开发者_如何学JAVAnum element. On that page there is also a function which returns the description's string based on the given enum. What I would like to implement now is the reverse function, that is, given an input string lookup the enum with the corresponding description if it exists, returning null otherwise.

I have tried (T) Enum.Parse(typeof(T), "teststring") but it throws an exception.


You have to write your own reverse method. The stock Parse() method obviously doesn't know about your description attributes.

Something like this should work:

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

You'll want to find a better thing to do than throw an exception if the enum value wasn't found, though. :)


You could also use Humanizer for that. To get the description you write:

EAssemblyUnit.eUCAL1.Humanize();

and to get the enum back from the description, which is what you want, you can write:

"UCAL1".DehumanizeTo<EAssemblyUnit>();

Disclaimer: I am the creator of Humanizer.


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

I have made no attempt at error checking. Note that the dictionary doesn't need to be created on every call to the method, but I'm too lazy to fix that.

Usage:

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

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

A test that passes:

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


I would have upvoted Anna's answer but I don't have the reputation to do so. With part of this based on her answer here's a 2-way solution that I came up with. Supplying a defaultValue to ParseEnum method covers cases where the same Enum may have a different default based on it's usage.

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


This answer to a related question shows how to retrieve the attributes for a given type. You might use a similar approach to compare a given string to an Enum's description attributes.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜