【问题标题】:Get enum from enum attribute从枚举属性中获取枚举
【发布时间】:2011-02-16 18:24:48
【问题描述】:

我有

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 中:

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

这可能吗?如果可以,怎么做? (StringValue 匹配从组合框中选择的值)。

【问题讨论】:

  • 应该可以。有什么问题?
  • 随机提示:您可以为此使用自动属性。使用 "public string Value { get; private set;} 可以避免俗气的 _value 变量。
  • @Kent Boogaart: "Niet beantwoord" != "NietBeantwoord"
  • 我认为如果您将枚举中的项目名称保留为组合框的 selectedvalue 属性,那么它应该可以工作(我想您知道这一点)。我现在确定为什么要将您定义的 stringvalue 属性定义为组合框的选定值。我想这是为了展示目的。
  • 您必须执行一个循环(或使用 LINQ 进行搜索),以找到输入文本的相应枚举值。使用 Oliver 的解决方案来获取属性。

标签: c# .net enums attributes custom-attributes


【解决方案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

【讨论】:

  • 这个解决方案可以很容易地改成通用的。只需将所有出现的Als 更改为T,并在方法中添加一个名为T 的类型参数。
  • @DanielHilgarth 我尝试这样做,但在方法的返回行上出现"The type parameter cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint" 错误。你能举一个实际的例子来说明如何解决这个问题吗?
【解决方案2】:

我正在使用 Microsoft 的 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;
}

【讨论】:

  • 与 OP 的要求相反,因为他想要字符串中的枚举而不是枚举中的字符串,但这对我有很大帮助,谢谢!
【解决方案3】:

这里有几个扩展方法,我用于这个确切的目的,我已经重写了这些以使用你的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()

【讨论】:

    【解决方案4】:

    来自this highly upvoted question and answer 的重复链接,这是一种适用于 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;
    }
    

    【讨论】:

      【解决方案5】:

      不确定我是否在这里遗漏了什么,你能不能不要这样做,

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

      【讨论】:

        【解决方案6】:

        要根据应用于枚举成员的属性值解析字符串值,我建议您使用我的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"
        

        【讨论】:

          【解决方案7】:

          我做了一个更通用的解决方案。
          您可以将它与 any 属性一起使用,甚至可以与字符串以外的其他类型一起使用。

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-20
            • 1970-01-01
            • 2012-12-28
            • 2017-01-14
            • 1970-01-01
            相关资源
            最近更新 更多