40

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.

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
DavidGouge
  • 4,583
  • 6
  • 34
  • 46

3 Answers3

47

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)

Community
  • 1
  • 1
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 1
    Ahh, LINQ to the rescue once again. I really like this solution, thanks! – DavidGouge Aug 06 '10 at 09:24
  • 1
    -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. – Henrik Mar 01 '11 at 13:45
  • @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. – Thomas Levesque Mar 01 '11 at 14:14
  • 1
    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. – Henrik Mar 02 '11 at 18:55
  • 2
    @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... – Thomas Levesque Mar 02 '11 at 21:08
  • 1
    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. – nawfal Jun 11 '13 at 01:05
  • @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? – nawfal Jun 11 '13 at 01:07
  • @nawfal, it doesn't really matter, since all enums inherit from Enum... And no, I don't have a benchmark – Thomas Levesque Jun 11 '13 at 08:07
  • @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. – nawfal Jun 11 '13 at 08:44
5
return field.GetRawConstantValue();

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

Ani
  • 111,048
  • 26
  • 262
  • 307
  • It looks promising, but it doesn't work. I get an `InvalidOperationException` : "Operation is not valid due to the current state of the object." – Thomas Levesque Aug 06 '10 at 09:31
  • Just had a look in Reflector: the only class that actually implements this method is `MdFieldInfo`. In `RtFieldInfo`, it just throws an InvalidOperationException. – Thomas Levesque Aug 06 '10 at 09:37
  • 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. – Ani Aug 06 '10 at 09:49
  • Indeed... the first field is `value__`, and it's not actually an enum member – Thomas Levesque Aug 06 '10 at 10:02
  • the proper way is to filter the actual `GetFields` call: `typeof(Testing).GetFields(BindingFlags.Public | BindingFlags.Static)` – Brad Nov 03 '14 at 13:52
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!

DavidGouge
  • 4,583
  • 6
  • 34
  • 46