257

Possible Duplicate:
Finding an enum value by its Description Attribute

I have a generic extension method which gets the Description attribute from an Enum:

enum Animal
{
    [Description("")]
    NotSet = 0,

    [Description("Giant Panda")]
    GiantPanda = 1,

    [Description("Lesser Spotted Anteater")]
    LesserSpottedAnteater = 2
}

public static string GetDescription(this Enum value)
{            
    FieldInfo field = value.GetType().GetField(value.ToString());

    DescriptionAttribute attribute
            = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
                as DescriptionAttribute;

    return attribute == null ? value.ToString() : attribute.Description;
}

so I can do...

string myAnimal = Animal.GiantPanda.GetDescription(); // = "Giant Panda"

now, I'm trying to work out the equivalent function in the other direction, something like...

Animal a = (Animal)Enum.GetValueFromDescription("Giant Panda", typeof(Animal));
0

6 Answers 6

388
public static class EnumEx
{
    public static T GetValueFromDescription<T>(string description) where T : Enum
    {
        foreach(var field in typeof(T).GetFields())
        {
            if (Attribute.GetCustomAttribute(field,
            typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
            {
                if (attribute.Description == description)
                    return (T)field.GetValue(null);
            }
            else
            {
                if (field.Name == description)
                    return (T)field.GetValue(null);
            }
        }

        throw new ArgumentException("Not found.", nameof(description));
        // Or return default(T);
    }
}

Usage:

var panda = EnumEx.GetValueFromDescription<Animal>("Giant Panda");
Sign up to request clarification or add additional context in comments.

10 Comments

If you add the "this" keyword in front of string ... public static T GetValueFromDescription<T>(this string description) ... it becomes an extension method and you can use syntax like: var x = "Giant Panda".GetValueFromDescription<Animal>();
How does one find if the description is not valid? with: var x = EnumEx.GetValueFromDescription<Animal>("Dinosaur"); I know it would throw an exception. But what would x contain?
@VenkatRenukaPrasad if it would throw anyway, then x cant have anything and you need not worry. Have an exception handling mechanism and handle it.
Might want to do case insensitive comparisons instead like this. if (string.Equals(attribute.Description,description, StringComparison.InvariantCultureIgnoreCase)) return (T)field.GetValue(null); It caught me out.
To make the above code work better, change the line "foreach(var field in type.GetFields())" to "foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))". Otherwise in the else below, the name of the field is 'value__' instead of the real name.
|
47

rather than extension methods, just try a couple of static methods

public static class Utility
{
    public static string GetDescriptionFromEnumValue(Enum value)
    {
        DescriptionAttribute attribute = value.GetType()
            .GetField(value.ToString())
            .GetCustomAttributes(typeof (DescriptionAttribute), false)
            .SingleOrDefault() as DescriptionAttribute;
        return attribute == null ? value.ToString() : attribute.Description;
    }

    public static T GetEnumValueFromDescription<T>(string description)
    {
        var type = typeof(T);
        if (!type.IsEnum)
            throw new ArgumentException();
        FieldInfo[] fields = type.GetFields();
        var field = fields
                        .SelectMany(f => f.GetCustomAttributes(
                            typeof(DescriptionAttribute), false), (
                                f, a) => new { Field = f, Att = a })
                        .Where(a => ((DescriptionAttribute)a.Att)
                            .Description == description).SingleOrDefault();
        return field == null ? default(T) : (T)field.Field.GetRawConstantValue();
    }
}

and use here

var result1 = Utility.GetDescriptionFromEnumValue(
    Animal.GiantPanda);
var result2 = Utility.GetEnumValueFromDescription<Animal>(
    "Lesser Spotted Anteater");

6 Comments

value.ToString() is expensive, do not call it twice (.. if it matters)...
@nawfal, expensive compared to what?
@MEMark I dont remember what I was thinking then, but may be this
If you change the method to add where T : Enum just before the open curly brace, you won't have to check the typeof(T) at runtime.
@ATD You're right, and I had forgotten that at the time. However, it is supported in the framework, so you can use Fody.ExtraConstraints to get the same effect, fully compiled and working as if where T : Enum had been specified. The syntax is public void MethodWithEnumConstraint<[EnumConstraint] T>() {...} which gets compiled to public void MethodWithEnumConstraint<T>() where T: struct, Enum {...}.
|
18

The solution works good except if you have a Web Service.

You would need to do the Following as the Description Attribute is not serializable.

[DataContract]
public enum ControlSelectionType
{
    [EnumMember(Value = "Not Applicable")]
    NotApplicable = 1,
    [EnumMember(Value = "Single Select Radio Buttons")]
    SingleSelectRadioButtons = 2,
    [EnumMember(Value = "Completely Different Display Text")]
    SingleSelectDropDownList = 3,
}

public static string GetDescriptionFromEnumValue(Enum value)
{
        EnumMemberAttribute attribute = value.GetType()
            .GetField(value.ToString())
            .GetCustomAttributes(typeof(EnumMemberAttribute), false)
            .SingleOrDefault() as EnumMemberAttribute;
        return attribute == null ? value.ToString() : attribute.Value;
}

3 Comments

what is the namespace of EnumMemberAttribute?
This is exactly what I am looking for. Two highlights I loved are, my enum also starts with 1. Secondly, its enummember as its wcf. Thank you so much :)
@AshishJain it's System.Runtime.Serialization
6

Should be pretty straightforward, its just the reverse of your previous method;

public static int GetEnumFromDescription(string description, Type enumType)
{
    foreach (var field in enumType.GetFields())
    {
        DescriptionAttribute attribute
            = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))as DescriptionAttribute;
        if(attribute == null)
            continue;
        if(attribute.Description == description)
        {
            return (int) field.GetValue(null);
        }
    }
    return 0;
}

Usage:

Console.WriteLine((Animal)GetEnumFromDescription("Giant Panda",typeof(Animal)));

Comments

3

You can't extend Enum as it's a static class. You can only extend instances of a type. With this in mind, you're going to have to create a static method yourself to do this; the following should work when combined with your existing method GetDescription:

public static class EnumHelper
{
    public static T GetEnumFromString<T>(string value)
    {
        if (Enum.IsDefined(typeof(T), value))
        {
            return (T)Enum.Parse(typeof(T), value, true);
        }
        else
        {
            string[] enumNames = Enum.GetNames(typeof(T));
            foreach (string enumName in enumNames)
            {  
                object e = Enum.Parse(typeof(T), enumName);
                if (value == GetDescription((Enum)e))
                {
                    return (T)e;
                }
            }
        }
        throw new ArgumentException("The value '" + value 
            + "' does not match a valid enum name or description.");
    }
}

And the usage of it would be something like this:

Animal giantPanda = EnumHelper.GetEnumFromString<Animal>("Giant Panda");

1 Comment

This is logically wrong (though can work in sensible scenarios). For instance if you have enum like Animal { ("Giant Panda")GiantPanda, ("GiantPanda")Tiger } and you call GetEnumFromString("GiantPanda"), you will get GiantPanda back, but I expect Tiger back. Have a function to either read enum string, or its description. Mixing both is a bad API style and can confuse clients. Or may be its good style to prevent user mistakes :)
1

You need to iterate through all the enum values in Animal and return the value that matches the description you need.

1 Comment

yes it's getting the right enum type that I'm a bit stuck on.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.