简体   繁体   中英

Get enum from enum attribute

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

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.

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

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()

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

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

Als temp = (Als)combo1.SelectedItem;
int t = (int)temp;

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"

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())
            {
                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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM