35

I've got

public enum Als 
{
    [StringValue("Beantwoord")] Beantwoord = 0,
    [StringValue("Niet beantwoord")] NietBeantwoord = 1,
    [StringValue("Geselecteerd")] Geselecteerd = 2,
    [StringValue("Niet geselecteerd")] NietGeselecteerd = 3,
}

with

public class StringValueAttribute : Attribute
{
    private string _value;

    public StringValueAttribute(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }
}

And I would like to put the value from the item I selected of a combobox into a int:

int i = (int)(Als)Enum.Parse(typeof(Als), (string)cboAls.SelectedValue); //<- WRONG

Is this possible, and if so, how? (the StringValue matches the value selected from the combobox).

nawfal
  • 70,104
  • 56
  • 326
  • 368
RubenHerman
  • 1,674
  • 6
  • 23
  • 42
  • 2
    That should work. What's the problem? – Kent Boogaart May 07 '10 at 09:30
  • 1
    Random tip: You can use auto properties for this. use "public string Value { get; private set;} and you can avoid the tacky _value variable. – Rubys May 07 '10 at 09:36
  • @Kent Boogaart: "Niet beantwoord" != "NietBeantwoord" – bniwredyc May 07 '10 at 09:36
  • I think if you keep the itemnames in the enum as the combobox's selectedvalue property then it should work (and I guess you know that). I am now sure why you want the stringvalue attribute you defined as the combobox's selected value. I suppose it is for display purpose. – 24x7Programmer May 07 '10 at 09:40
  • You have to do a loop (or use LINQ for the search) which finds the corresponding enum value to the entered text. Use Oliver's solution to get the Attributes. – codymanix May 07 '10 at 09:46
  • duplicate of http://stackoverflow.com/questions/1608000/enumeration-utility-library?lq=1. Not enough reason to close though.. – nawfal Jun 09 '13 at 12:03

7 Answers7

22

Here's a helper method that should point you in the right direction.

protected Als GetEnumByStringValueAttribute(string value)
{
    Type enumType = typeof(Als);
    foreach (Enum val in Enum.GetValues(enumType))
    {
        FieldInfo fi = enumType.GetField(val.ToString());
        StringValueAttribute[] attributes = (StringValueAttribute[])fi.GetCustomAttributes(
            typeof(StringValueAttribute), false);
        StringValueAttribute attr = attributes[0];
        if (attr.Value == value)
        {
            return (Als)val;
        }
    }
    throw new ArgumentException("The value '" + value + "' is not supported.");
}

And to call it, just do the following:

Als result = this.GetEnumByStringValueAttribute<Als>(ComboBox.SelectedValue);

This probably isn't the best solution though as it's tied to Als and you'll probably want to make this code re-usable. What you'll probably want to strip out the code from my solution to return you the attribute value and then just use Enum.Parse as you are doing in your question.

ysrb
  • 6,693
  • 2
  • 29
  • 30
djdd87
  • 67,346
  • 27
  • 156
  • 195
  • 9
    This solution can easily be changed to be generic. Simply change all occurrences of `Als` to `T` and add a type argument named `T` to the method. – Daniel Hilgarth Jan 23 '12 at 14:23
  • @DanielHilgarth I tryied to do it but got error saying `"The type parameter cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint"` on the return line of the method. Would you have a practical example on how this could be solved? – Ulysses Alves Aug 31 '17 at 20:22
10

I'm using the DescriptionAttribute from Microsoft and the following extension method:

public static string GetDescription(this Enum value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }

    string description = value.ToString();
    FieldInfo fieldInfo = value.GetType().GetField(description);
    DescriptionAttribute[] attributes =
       (DescriptionAttribute[])
     fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

    if (attributes != null && attributes.Length > 0)
    {
        description = attributes[0].Description;
    }
    return description;
}
Oliver
  • 43,366
  • 8
  • 94
  • 151
  • 7
    Opposite of what OP asked since he wants enum from string and not string from enum, but that helped me a lot, thanks! – psycho Mar 14 '13 at 08:29
6

Here are a couple extension methods that I use for this exact purpose, I've rewritten these to use your StringValueAttribute, but like Oliver I use the DescriptionAttribute in my code.

    public static T FromEnumStringValue<T>(this string description) where T : struct {
        Debug.Assert(typeof(T).IsEnum);

        return (T)typeof(T)
            .GetFields()
            .First(f => f.GetCustomAttributes(typeof(StringValueAttribute), false)
                         .Cast<StringValueAttribute>()
                         .Any(a => a.Value.Equals(description, StringComparison.OrdinalIgnoreCase))
            )
            .GetValue(null);
    }

This can be made slightly more simple in .NET 4.5:

    public static T FromEnumStringValue<T>(this string description) where T : struct {
        Debug.Assert(typeof(T).IsEnum);

        return (T)typeof(T)
            .GetFields()
            .First(f => f.GetCustomAttributes<StringValueAttribute>()
                         .Any(a => a.Value.Equals(description, StringComparison.OrdinalIgnoreCase))
            )
            .GetValue(null);
    }

And to call it, just do the following:

Als result = ComboBox.SelectedValue.FromEnumStringValue<Als>();

Conversely, here's a function to get the string from an enum value:

    public static string StringValue(this Enum enumItem) {
        return enumItem
            .GetType()
            .GetField(enumItem.ToString())
            .GetCustomAttributes<StringValueAttribute>()
            .Select(a => a.Value)
            .FirstOrDefault() ?? enumItem.ToString();
    }

And to call it:

string description = Als.NietBeantwoord.StringValue()
Community
  • 1
  • 1
joshperry
  • 41,167
  • 16
  • 88
  • 103
2

Coming here from duplicate links of this highly upvoted question and answer, here is a method that works with C# 7.3's new Enum type constraint. Note that you also need to specify that it is also a struct so that you can make it the nullable TEnum? or else you will get an error.

public static TEnum? GetEnumFromDescription<TEnum>(string description)
    where TEnum : struct, Enum 
{
    var comparison = StringComparison.OrdinalIgnoreCase;
    foreach (var field in typeof(TEnum).GetFields())
    {
        var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
        if (attribute != null)
        {
            if (string.Compare(attribute.Description, description, comparison) == 0)
                return (TEnum)field.GetValue(null);
        }
        if (string.Compare(field.Name, description, comparison) == 0)
            return (TEnum)field.GetValue(null);
    }
    return null;
}
DLeh
  • 23,806
  • 16
  • 84
  • 128
1

Not sure if I am missing something here, can you not do this,

Als temp = (Als)combo1.SelectedItem;
int t = (int)temp;
theraneman
  • 1,620
  • 4
  • 18
  • 32
0

To parse a string value based on attribute values applied to enum members I'd recommend you use my Enums.NET open source library.

For a custom attribute like the StringValueAttribute you would do this.

Register and store a custom EnumFormat for StringValueAttribute.Value.

Format = Enums.RegisterCustomEnumFormat(m => m.Attributes.Get<StringValueAttribute>()?.Value);

Then use the custom EnumFormat.

Als result = Enums.Parse<Als>("Niet beantwoord", Format); // result == Als.NietBeantwoord

If you were instead using a built-in attribute such as the DescriptionAttribute you could just do this.

Als result = Enums.Parse<Als>("Niet beantwoord", EnumFormat.Description);

In case you're interested, this is how you'd get the string value associated with an enum value.

string value = Als.NietBeantwoord.AsString(Format); // value == "Niet beantwoord"
TylerBrinkley
  • 1,066
  • 10
  • 16
0

I made a more generic solution.
You can use it with any attribute and even with other types than string.

using System;

namespace EnumTest
{
    public static class EnumExtensions
    {
        #region Get enum from Attribute
        /// <summary>
        /// Searches the enum element which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
        /// Throws exception, if there is no enum element found which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
        /// </summary>
        /// <param name="attributeType">the type of the attribute. e.g. typeof(System.ComponentModel.DescriptionAttribute)</param>
        /// <param name="attributePropertyName">the property of the attribute to search. At DescriptionAttribute, this is "Description"</param>
        /// <param name="searchValue">the value to search</param>
        public static TEnum FromAttributeValueToEnum<TEnum>(Type attributeType, string attributePropertyName, object searchValue)
        where TEnum : struct, Enum
        {
            TEnum? result = FromAttributeValueToNullableEnum<TEnum>(attributeType, attributePropertyName, searchValue);

            if (result.HasValue)
            {
                return result.Value;
            }

            Type enumType = typeof(TEnum);
            throw new ArgumentException($"The enum type {enumType.FullName} has no element with a {attributeType.FullName} with {attributePropertyName} property equivalent to '{searchValue}'");
        }

        /// <summary>
        /// Searches the enum element which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
        /// Returns fallBackValue, if there is no enum element found which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
        /// </summary>
        /// <param name="attributeType">the type of the attribute. e.g. typeof(System.ComponentModel.DescriptionAttribute)</param>
        /// <param name="attributePropertyName">the property of the attribute to search. At DescriptionAttribute, this is "Description"</param>
        /// <param name="searchValue">the value to search</param> 
        public static TEnum FromAttributeValueToEnum<TEnum>(Type attributeType, string attributePropertyName, object searchValue, TEnum fallBackValue)
        where TEnum : struct, Enum
        {
            TEnum? result = FromAttributeValueToNullableEnum<TEnum>(attributeType, attributePropertyName, searchValue);

            if (result.HasValue)
            {
                return result.Value;
            }

            return fallBackValue;
        }

        /// <summary>
        /// Searches the enum element which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
        /// Returns null, if there is no enum element found which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
        /// </summary>
        /// <param name="attributeType">the type of the attribute. e.g. typeof(System.ComponentModel.DescriptionAttribute)</param>
        /// <param name="attributePropertyName">the property of the attribute to search. At DescriptionAttribute, this is "Description"</param>
        /// <param name="searchValue">the value to search</param> 
        public static TEnum? FromAttributeValueToNullableEnum<TEnum>(Type attributeType, string attributePropertyName, object searchValue)
        where TEnum : struct, Enum
        {
            Type enumType = typeof(TEnum);
            if (!enumType.IsEnum)
            {
                throw new ArgumentException($"The type {enumType.FullName} is no Enum!");
            }

            if (attributeType == null)
            {
                throw new ArgumentNullException(nameof(attributeType));
            }

            if (!typeof(Attribute).IsAssignableFrom(attributeType))
            {
                throw new ArgumentException($"The type {attributeType.FullName} is no Attribute!", nameof(attributeType));
            }

            var propertyInfoAttributePropertyName = attributeType.GetProperty(attributePropertyName);

            if (propertyInfoAttributePropertyName == null)
            {
                throw new ArgumentException($"The type {attributeType.FullName} has no (public) property with name {attributePropertyName}", nameof(attributeType));
            }

            foreach (var field in enumType.GetFields())
            {
                if (field.IsSpecialName)
                {
                    continue;
                }

                var attributes = Attribute.GetCustomAttributes(field, attributeType);
                if (attributes.Length == 0)
                {
                    continue;
                }

                foreach (var attribute in attributes)
                {
                    object attributePropertyValue = propertyInfoAttributePropertyName.GetValue(attribute);

                    if (attributePropertyValue == null)
                    {
                        continue;
                    }

                    if (attributePropertyValue.Equals(searchValue))
                    {
                        return (TEnum)field.GetValue(null);
                    }
                }
            }

            return null;
        }
        #endregion
    }
}

@RubenHerman's initial post:

Als Beantwoord = EnumExtensions.FromAttributeValueToEnum<Als>(typeof(StringValueAttribute), nameof(StringValueAttribute.Value), "Beantwoord");

Advanced sample:

public class IntValueAttribute : Attribute
{
    public IntValueAttribute(int value)
    {
        Value = value;
    }

    public int Value { get; private set; }
}

public enum EnumSample
{
    [Description("Eins")]
    [IntValue(1)]
    One,

    [Description("Zwei")]
    [IntValue(2)]
    Two,

    [Description("Drei")]
    [IntValue(3)]
    Three
}

EnumSample one = EnumExtensions.FromAttributeValueToEnum<EnumSample>(typeof(DescriptionAttribute), nameof(DescriptionAttribute.Description), "Eins");
EnumSample two = EnumExtensions.FromAttributeValueToEnum<EnumSample>(typeof(IntValueAttribute), nameof(IntValueAttribute.Value), 2);
Joseph K
  • 1
  • 1