【问题标题】:Parse string to enum type将字符串解析为枚举类型
【发布时间】:2009-09-15 03:30:01
【问题描述】:

我有一个像这样的枚举类型作为例子:

public Enum MyEnum {
    enum1, enum2, enum3 };

我将从配置文件中读取一个字符串。我需要将字符串解析为 MyEnum 类型或 null 或未定义。不确定以下代码是否有效(抱歉,我现在无法访问我的 VS):

// example: ParseEnum<MyEnum>("ENUM1", ref eVal);
bool ParseEnum<T>(string value1, ref eVal) where T : Enum
{
  bool bRet = false;
  var x = from x in Enum.GetNames(typeof(T)) where 
       string.Equals(value1, x, StringComparison. OrdinalIgnoreCase)
       select x;
  if (x.Count() == 1 )
  {
    eVal = Enum.Parse(typeof(T), x.Item(0)) as T;
    bRet = true;
  }
  return bRet;
}

不确定它是否正确或有任何其他简单的方法可以将字符串解析为 MyEnum 值?

【问题讨论】:

    标签: c# .net generics enums


    【解决方案1】:

    类似的东西呢:

    public static class EnumUtils
    {
        public static Nullable<T> Parse<T>(string input) where T : struct
        {
            //since we cant do a generic type constraint
            if (!typeof(T).IsEnum)
            {
                throw new ArgumentException("Generic Type 'T' must be an Enum");
            }
            if (!string.IsNullOrEmpty(input))
            {
                if (Enum.GetNames(typeof(T)).Any(
                      e => e.Trim().ToUpperInvariant() == input.Trim().ToUpperInvariant()))
                {
                    return (T)Enum.Parse(typeof(T), input, true);
                }
            }
            return null;
        }
    }
    

    用作:

    MyEnum? value = EnumUtils.Parse<MyEnum>("foo");
    

    (注意:旧版本使用try/catch 周围Enum.Parse

    【讨论】:

    • @Mark 我知道,但在这种情况下确实很容易 :)
    • @Mark 确实提出了一个很好的观点 - try/catch 很少是一个好主意。编辑后的版本比问题中的版本效率高得多,因为它只进行一次部分枚举,而不是最多 3 次。
    • 确保 T 的泛型类型约束:Enum 在这里也很有用,否则 Enum.GetNames 可能会抛出。
    • @Ty 不幸的是这是不可能的 - 类型约束不能是枚举。
    • 一项改进是将 input.ToUpperInvariant() 更改为 (input ?? "").Trim()。 ToUpperInvariant()。这将避免空异常。
    【解决方案2】:
    private enum MyEnum
    {
        Enum1 = 1, Enum2 = 2, Enum3 = 3, Enum4 = 4, Enum5 = 5, Enum6 = 6, 
        Enum7 = 7, Enum8 = 8, Enum9 = 9, Enum10 = 10
    }
    
    private static Object ParseEnum<T>(string s)
    {
        try
        {
            var o = Enum.Parse(typeof (T), s);
            return (T)o;
        }
        catch(ArgumentException)
        {
            return null;
        }
    }
    
    static void Main(string[] args)
    {
       Console.WriteLine(ParseEnum<MyEnum>("Enum11"));
       Console.WriteLine(ParseEnum<MyEnum>("Enum1"));
       Console.WriteLine(ParseEnum<MyEnum>("Enum6").GetType());
       Console.WriteLine(ParseEnum<MyEnum>("Enum10"));
    }
    

    输出:

        //This line is empty as Enum11 is not there and function returns a null
    Enum1
    TestApp.Program+MyEnum
    Enum10
    Press any key to continue . . .
    

    【讨论】:

      【解决方案3】:

      这是一个老问题,但现在 .NET 4.5 有 Enum.TryParse&lt;TEnum&gt;()

      【讨论】:

      • 这样的问题的答案,永远不会老:)
      【解决方案4】:

      我在UnconstrainedMelody 中有一个TryParseName 方法,这是一个用于委托和枚举实用程序方法的库,它通过一些构建后的技巧使用“无法表达”的约束。 (代码使用库不需要后期构建,只是为了清楚。)

      你会这样使用它:

      Foo foo;
      bool parsed = Enums.TryParseName<Foo>(name, out foo);
      

      我目前没有不区分大小写的版本,但如果您愿意,我可以轻松介绍一个。请注意,这 不会 尝试解析数字,例如"12" 像内置版本一样,也不会尝试解析以逗号分隔的标志列表。稍后我可能会添加标志版本,但我在数字版本中看不到太多意义。

      这是在没有装箱和执行时间类型检查的情况下完成的。有约束真的很方便:)

      如果您发现不区分大小写的解析有用,请告诉我...

      【讨论】:

      • @Jon Skeet,你可以引入额外的重载方法让用户可以选择是否区分大小写。
      • @Joh Skeet,不确定您的方法是否应该是 (name, ref foo)。如果 tryparse 失败, foo 应该是什么?第一个枚举值?我认为最好让用户初始化它,如果失败则不要更改它。我了解您尝试使此方法与 TryParse(name, out value) 保持一致。
      • 如果TryParse 失败,它将是default(Foo),这与TryParseTryGetValue 等一致。如果我不追求一致性,我可能会返回Nullable&lt;T&gt;反而。我将考虑为不区分大小写的匹配引入一个新的重载 - 或者可能采用 StringComparer(或类似的)来允许选择文化敏感性。
      【解决方案5】:

      我刚刚将here 的语法与here 的异常处理结合起来,创建了这个:

      public static class Enum<T>
      {
          public static T Parse(string value)
          {
              //Null check
              if(value == null) throw new ArgumentNullException("value");
              //Empty string check
              value = value.Trim();
              if(value.Length == 0) throw new ArgumentException("Must specify valid information for parsing in the string", "value");
              //Not enum check
              Type t = typeof(T);
              if(!t.IsEnum) throw new ArgumentException("Type provided must be an Enum", "TEnum");
      
              return (T)Enum.Parse(typeof(T), value);
          }
      }
      

      您可以稍微调整一下以返回 null 而不是抛出异常。

      【讨论】:

      • @Benjol,我喜欢你使用 type.IsEnum 来检查它的类型。
      【解决方案6】:

      如果您使用的是 .NET 3.5(或者甚至是 2.0,如果您去掉了扩展方法),我对本文中的技术非常幸运:

      枚举和字符串 - 停止疯狂!

      编辑:域已经消失,现在是一个链接场。我在工作时从我们的代码库中提取了代码(随着时间的推移略有修改和添加),您现在可以在这里找到:

      https://gist.github.com/1305566

      【讨论】:

      • 引用现在指向链接场。我最初为你修改了一个,但你的声望很高,所以我猜你不是故意这样做的,你不应该因为超出你控制范围的内容而受到惩罚。我自己搜索了这篇文章,但没有发现任何明显的东西。你能看看这个内容是否有更好的链接吗?
      • @MichaelBlackburn:看起来拥有该域的人消失了。我将在星期一检查我们的代码库,看看是否能找到我从文章中借来的内容。
      • 粘贴整个解决方案而不是链接的另一个原因。
      【解决方案7】:

      如果你想避免使用 try/catch,你可以使用TryParse

      MyEnum eVal;
      if (Enum.TryParse("ENUM2", true, out eVal)){
          // now eVal is the enumeration element: enum2 
      }
      //unable to parse. You can log the error, exit, redirect, etc...
      

      我稍微修改了选定的答案。希望你喜欢。

      public static class EnumUtils
      {
          public static Nullable<T> Parse<T>(string input) where T : struct
          {
              //since we cant do a generic type constraint
              if (!typeof(T).IsEnum)
              {
                  throw new ArgumentException("Generic Type 'T' must be an Enum");
              }
      
              int intVal;
              if (!string.IsNullOrEmpty(input) && !int.TryParse(input, out intVal))
              {
                  T eVal;
                  if (Enum.TryParse(input, true, out eVal))
                  {
                      return eVal;
                  }
              }
              return null;
          }
      }
      

      【讨论】:

        【解决方案8】:

        通过字符串返回枚举,如果包含:

            public static T GetEnum<T>(string s)
            {
                Array arr = Enum.GetValues(typeof(T));
                foreach (var x in arr)
                {
                    if (x.ToString().Contains(s))
                        return (T)x;
                }
                return default(T);
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-14
          • 2010-10-25
          • 1970-01-01
          • 2014-07-23
          相关资源
          最近更新 更多