Получить перечисление из атрибута enum

У меня есть

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

с

public class StringValueAttribute : Attribute
{
    private string _value;

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

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

И я хотел бы поместить значение из элемента, который я выбрал из combobox, в int:

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

Это возможно, и если да, то как? (StringValue соответствует значению, выбранному из выпадающего списка).

Ответ 1

Вот вспомогательный метод, который должен указывать вам в правильном направлении.

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.");
}

И чтобы вызвать его, просто выполните следующие действия:

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

Это, вероятно, не лучшее решение, хотя оно связано с Als, и вы, вероятно, захотите сделать этот код повторно используемым. Что вы, вероятно, захотите удалить из моего решения код, чтобы вернуть вам значение атрибута, а затем просто используйте Enum.Parse, как вы делаете в своем вопросе.

Ответ 2

Я использую DescriptionAttribute от Microsoft и следующий метод расширения:

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

Ответ 3

Вот несколько методов расширения, которые я использую для этой конкретной цели, я переписал их, чтобы использовать ваш StringValueAttribute, но, как DescriptionAttribute в моем коде.

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

Это можно сделать несколько проще в .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);
    }

И чтобы вызвать его, просто выполните следующие действия:

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

И наоборот, здесь функция, чтобы получить строку из значения перечисления:

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

И чтобы называть его:

string description = Als.NietBeantwoord.StringValue()

Ответ 4

Не уверен, что здесь что-то не хватает, не можете ли вы это сделать,

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

Ответ 5

Попадая сюда из повторяющихся звеньев этого весьма upvoted вопроса и ответа, вот метод, который работает с С# 7.3 нового Enum типа ограничением. Обратите внимание, что вам также нужно указать, что это также struct чтобы вы могли сделать ее TEnum? или вы получите сообщение об ошибке.

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

Ответ 6

Чтобы проанализировать строковое значение на основе значений атрибутов, примененных к членам перечисления, я бы порекомендовал вам использовать мою библиотеку с открытым исходным кодом Enums.NET.

Для пользовательского атрибута, такого как StringValueAttribute вы должны сделать это.

Зарегистрируйте и сохраните пользовательский EnumFormat для StringValueAttribute.Value.

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

Затем используйте пользовательский EnumFormat.

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

Если бы вместо этого вы использовали встроенный атрибут, такой как DescriptionAttribute вы могли бы просто сделать это.

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

В случае, если вам интересно, вы получите строковое значение, связанное со значением перечисления.

string value = Als.NietBeantwoord.AsString(Format); // value == "Niet beantwoord"

Ответ 7

Я сделал более общее решение.
Вы можете использовать его с любым атрибутом и даже с другими типами, кроме строки.

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 attribute = Attribute.GetCustomAttribute(field, attributeType);
                if (attribute == null)
                {
                    continue;
                }

                object attributePropertyValue = propertyInfoAttributePropertyName.GetValue(attribute);

                if (attributePropertyValue == null)
                {
                    continue;
                }

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

            return null;
        }
        #endregion
    }
}

@RubenHerman начальное сообщение:

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

Расширенный образец:

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