Finding an enum value by its Description Attribute [duplicate]
Asked Answered
G

3

41

This may seem a little upside down faced, but what I want to be able to do is get an enum value from an enum by its Description attribute.

So, if I have an enum declared as follows:

enum Testing
{
    [Description("David Gouge")]
    Dave = 1,
    [Description("Peter Gouge")]
    Pete = 2,
    [Description("Marie Gouge")]
    Ree = 3
}

I'd like to be able to get 2 back by supplying the string "Peter Gouge".

As a starting point, I can iterate through the enum fields and grab the field with the correct attribute:

string descriptionToMatch = "Peter Gouge";
FieldInfo[] fields = typeof(Testing).GetFields();

foreach (FieldInfo field in fields)
{
    if (field.GetCustomAttributes(typeof(DescriptionAttribute), false).Count() > 0)
    {
        if (((DescriptionAttribute)field.GetCustomAttributes(typeof(DescriptionAttribute), false)[0]).Description == descriptionToMatch)
        {

        }
    }
}

But then I'm stuck as to what to do in that inner if. Also not sure if this is the way to go in the first place.

Guise answered 6/8, 2010 at 9:13 Comment(0)
A
51

Using the extension method described here :

Testing t = Enum.GetValues(typeof(Testing))
                .Cast<Testing>()
                .FirstOrDefault(v => v.GetDescription() == descriptionToMatch);

If no matching value is found, it will return (Testing)0 (you might want to define a None member in your enum for this value)

Acarid answered 6/8, 2010 at 9:17 Comment(9)
Ahh, LINQ to the rescue once again. I really like this solution, thanks!Guise
-1: extra objects created in this code: 1) a RuntimeType, 2) a cast-iterator, 3) a lambda object, 4) a WhereIterator. Extra objects created with Ani's solution; none.Salcedo
@Henrik, you're just forgetting one thing: Ani's solution assumes you already have the FieldInfo, and you need to use reflection to obtain it... My solution doesn't use reflection; GetValues is implemented internally with native code, so it's much faster than using reflection (at least 5x faster according to my tests). The cost of creating a RuntimeType and a WhereIterator is negligible compared to reflection. As for the "lambda object", there is no such thing... it's just an anonymous method created in the current type.Acarid
Yes, your method does use reflection, look at what you linked to, so you do need to get the FieldInfo. You are not correct about the non-object-ness of the anonymous method (or lambda or whatever) as you are doing a closure capture over "descriptionToMatch" which has to capture its state references privately.Salcedo
@Henrik, yes, the GetDescription method does use reflection, I forgot that... but it's the only way you can access the attribute anyway. And the closure does generate a new type; I was wrong when I said that it was a method in the same object, I forgot about the closure. It's just the term "lambda object" that bothered me, because I don't think it's accurate, although I understand your meaning now... Anyway, what's so wrong with creating new objects ? Ani's solution doesn't fully answer the question, it only gives the value of a specific enum value...Acarid
Not to be nitpicking, but pointing it out if it helps anyone - for your v.GetDescription to work, you should Cast<> to Enum type, not Testing since that's what the GetDescription extension method accepts.Ardolino
@ThomasLevesque 1) Ani's solution fully answers the question, its only a matter of casting the value to enum type (which he mentioned in the question), 2) I did a quick round of tests and found his approach to be faster, though its less than 100% improvement.. Do you have some benchmarks to prove otherwise?Ardolino
@nawfal, it doesn't really matter, since all enums inherit from Enum... And no, I don't have a benchmarkAcarid
@ThomasLevesque well, I didn't know Testing was an enum, thought it could be type parameter :), btw its hard to believe a "5x improvement" with this approach.Ardolino
A
5
return field.GetRawConstantValue();

You could of course cast it back to Testing if required.

Alevin answered 6/8, 2010 at 9:18 Comment(5)
It looks promising, but it doesn't work. I get an InvalidOperationException : "Operation is not valid due to the current state of the object."Acarid
Just had a look in Reflector: the only class that actually implements this method is MdFieldInfo. In RtFieldInfo, it just throws an InvalidOperationException.Acarid
It works fine; you have to skip the first result in the enumeration. typeof(Testing).GetFields[1].GetRawConstantValue(); Alternatively, you could filter on type MdFieldInfo as suggested by Thomas. This is probably a better solution.Alevin
Indeed... the first field is value__, and it's not actually an enum memberAcarid
the proper way is to filter the actual GetFields call: typeof(Testing).GetFields(BindingFlags.Public | BindingFlags.Static)Sayce
G
2

Ok, after typing all that I think this is a case of a decision right at the beginning leading me down the wrong path. Enum seemed the right way to go to start with, but a simple Dictionary<string, int> will suffice and be a hell of a lot easier to work with!

Guise answered 6/8, 2010 at 9:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.