从枚举属性获取枚举值

35

我已经拥有了

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

我想把我从下拉框中选择的项目的值放入一个整数中:

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

这个可能吗?如果可能,应该怎么做?(StringValue应该与从下拉框中选择的值匹配)。


2
应该可以了。问题出在哪里? - Kent Boogaart
1
随机提示:您可以使用自动属性来实现此功能。使用“public string Value { get; private set;}”即可避免使用俗气的“_value”变量。 - Rubys
@Kent Boogaart: "Niet beantwoord"!= "NietBeantwoord" - bniwredyc
我认为如果你将枚举中的项目名称保持为组合框的选定值属性,那么它应该可以工作(我猜你知道这一点)。我不确定你为什么想要你定义的字符串值属性作为组合框的选定值。我想这是为了显示目的。 - 24x7Programmer
你需要编写一个循环(或使用LINQ搜索)来查找与输入文本对应的枚举值。使用Oliver的解决方案获取属性。 - codymanix
重复的问题,请参考 http://stackoverflow.com/questions/1608000/enumeration-utility-library?lq=1。虽然没有足够的理由关闭它。 - nawfal
7个回答

22

下面是一个帮助方法,应该能指导你朝着正确的方向前进。

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


9
这个解决方案可以轻松地更改为通用型。只需将所有的“Als”更改为“T”,并在该方法中添加一个名为“T”的类型参数即可。 - Daniel Hilgarth
@DanielHilgarth 我尝试过,但在方法的返回行上出现了错误,提示“类型参数不能与'as'运算符一起使用,因为它没有类类型约束或'class'约束”。您有实际的例子可以解决这个问题吗? - Ulysses Alves

10

我正在使用来自微软的DescriptionAttribute以及下面的拓展方法:

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

7
与 OP 所要求的相反,因为他想要从字符串获取枚举而不是从枚举获取字符串,但这对我非常有帮助,谢谢! - psycho

6
这里是我用于此目的的一些扩展方法,我已重写它们以使用您的 StringValueAttribute,但像 Oliver 一样,我在我的代码中使用 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()

2

如果你从这个高赞的问题和答案链接跳转而来,这里提供了一种使用C# 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;
}

1

不确定我是否遗漏了什么,您不能这样做吗?

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

0

如果基于应用于枚举成员的属性值来解析字符串值,我建议您使用我的开源库Enums.NET

对于像StringValueAttribute这样的自定义属性,您需要执行以下操作:

StringValueAttribute.Value注册并存储自定义EnumFormat

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"

0

我制作了一个更加通用的解决方案。
你可以使用它来处理任何属性,甚至是其他类型而不仅仅是字符串。

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的初始帖子:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接