Miguel
Miguel

Reputation: 3506

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 enum 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.

Upvotes: 12

Views: 15574

Answers (5)

Mehdi Khalili
Mehdi Khalili

Reputation: 937

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.

Upvotes: 3

SharePimp
SharePimp

Reputation: 11

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

Upvotes: 1

Adam Lear
Adam Lear

Reputation: 38778

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. :)

Upvotes: 19

jason
jason

Reputation: 241789

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

Upvotes: 2

Dan J
Dan J

Reputation: 16728

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.

Upvotes: 0

Related Questions