我应该如何将字符串转换为C#中的枚举?

Ben*_*lls 797 c# string enums

在C#中将字符串转换为枚举值的最佳方法是什么?

我有一个包含枚举值的HTML选择标记.发布页面时,我想获取值(将以字符串的形式)并将其转换为枚举值.

在一个理想的世界里,我可以这样做:

StatusEnum MyStatus = StatusEnum.Parse("Active");
Run Code Online (Sandbox Code Playgroud)

但那不是有效的代码.

Kei*_*ith 1362

在.NET Core和.NET> 4中,有一个通用的解析方法:

Enum.TryParse("Active", out StatusEnum myStatus);
Run Code Online (Sandbox Code Playgroud)

这还包括C#7的新内联out变量,因此这将执行try-parse,转换为显式枚举类型并初始化+填充myStatus变量.

如果您可以访问C#7和最新的.NET,这是最好的方法.

原始答案

在.NET中它相当丑陋(直到4或以上):

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);
Run Code Online (Sandbox Code Playgroud)

我倾向于简化这个:

public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}
Run Code Online (Sandbox Code Playgroud)

然后我可以这样做:

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");
Run Code Online (Sandbox Code Playgroud)

评论中建议的一个选项是添加一个扩展,这很简单:

public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();
Run Code Online (Sandbox Code Playgroud)

最后,如果无法解析字符串,您可能希望使用默认枚举:

public static T ToEnum<T>(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}
Run Code Online (Sandbox Code Playgroud)

这使得这个电话:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);
Run Code Online (Sandbox Code Playgroud)

但是,我会小心地将这样的扩展方法添加到string(没有命名空间控制)它将出现在所有实例上,string无论它们是否包含枚举(因此1234.ToString().ToEnum(StatusEnum.None)有效但无意义).通常最好避免使用仅在非常特定的上下文中应用的额外方法来混淆Microsoft的核心类,除非您的整个开发团队非常了解这些扩展的作用.

  • @avinashr对@ McKenzieG1的回答是正确的,但它并不总是重要的.例如,如果您为每个解析进行数据库调用,那么担心枚举解析将是一个毫无意义的微优化. (25认同)
  • 非常好.你需要在上一个例子中使用where T:struct. (14认同)
  • 如果性能很重要(通常是)下面的Mckenzieg1给出的答案:http://stackoverflow.com/questions/16100/converting-a-string-to-an-enumeration-value-in-c/38711#38711 (13认同)
  • Enum.TryParse怎么样? (7认同)
  • @HM我不认为扩展在这里是合适的 - 这是一个特殊情况,扩展将适用于_every_ string.如果你真的想这样做,虽然这将是一个微不足道的变化. (4认同)
  • @bump 是的 - 作为一般规则,你必须非常小心地扩展 Microsoft 的内置对象,尤其是非常常见的对象,如“object”或“string”,因为如果没有仔细的命名空间控制,这将导致你的扩展方法在跨域的每个实例上都可见。您的整个代码库。当方法始终合适时(例如“.Trim()”或“.Split()”),这不是问题,但枚举解析仅在预期字符串包含枚举时才会应用。像这样的一两个方法可能没问题,但在大型项目中您可能会遇到大量令人困惑的方法。 (2认同)
  • @Keith 好的,但这仅适用于命名空间包含在使用权中的情况?那么,“namespace SomeNameSpace.Extensions.Strings”不会导致任何问题吗? (2认同)
  • @bump 是的,正是如此 - 这就是我所说的命名空间控制的意思。您可以将这些扩展放入命名空间中以选择它们可用 - 这是便利性和代码标准化之间的平衡,这在很大程度上取决于您的开发团队。 (2认同)
  • @barrypicker 看上面的评论。`public static T ToEnum&lt;T&gt;(this string value, T defaultValue) where T : struct` (2认同)
  • 在扩展方法定义的末尾需要添加`T:struct,IComparable`。 (2认同)

Erw*_*yer 306

使用Enum.TryParse<T>(String, T)(≥.NET4.0):

StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);
Run Code Online (Sandbox Code Playgroud)

使用C#7.0的参数类型内联可以进一步简化:

Enum.TryParse("Active", out StatusEnum myStatus);
Run Code Online (Sandbox Code Playgroud)

  • 为区分大小写添加中间布尔参数,这是迄今为止最安全,最优雅的解决方案. (43认同)
  • 来吧,你们中有多少人从2008年开始实施所选答案,只向下滚动,发现这是更好(现代)的答案. (15认同)
  • Enum.TryParse&lt;T&gt;(String, T) 在解析整数字符串时存在缺陷。例如,此代码将成功地将无意义的字符串解析为无意义的枚举:`var result = Enum.TryParse&lt;System.DayOfWeek&gt;("55", out var parsedEnum);` (2认同)
  • @MassDotNet 在这种情况下添加: `&amp;&amp; Enum.IsDefined(typeof(System.DayOfWeek), parsedEnum)` 以确保解析的 Enum 实际存在。 (2认同)

McK*_*eG1 186

请注意,Enum.Parse()的性能很糟糕,因为它是通过反射实现的.(对于Enum.ToString也是如此,这是另一种方式.)

如果您需要在性能敏感的代码中将字符串转换为枚举,最好的办法是Enum.Parse()在启动时创建并使用它来进行转换.

  • 哇3ms是可怕的数量级 (10认同)
  • 我已经测量了3ms,在第一次运行时,在桌面计算机上将字符串转换为Enum.(只是为了说明可怕的程度). (6认同)
  • 你能在这个周围添加一个代码示例,以便我们了解如何替换和使用 (3认同)
  • Enum.Parse 需要纳秒,而不是毫秒,至少使用现代 .NET 版本是这样。我在 .NET 6 中使用 BenchmarkDotNet 测试了此代码: `MyEnum myEnum = Enum.Parse&lt;MyEnum&gt;("Seven");` 它产生了以下结果: Mean: **33.19 ns** Error: 0.469 ns StdDev: 0.392 ns (3认同)
  • .NET Framework 几乎与 .NET 6 一样快,在所有 .NET Framework 版本 4.6.1-4.8.1 上以 **175 ns** 运行以下代码: `Enum.TryParse&lt;MyEnum&gt;(Input, out MyEnum结果);`。这并不“可怕”。 (3认同)
  • 如果您的应用程序有 100 万人使用 =&gt; 您在单个页面上使用的时间总计为 50 个小时:)。:P (2认同)
  • 虽然第一次运行 3 毫秒确实很糟糕,但第二次运行会更好吗?如果每次都是 3ms 那么我们就会像瘟疫一样避免它 (2认同)
  • 我还使用变量“myString”而不是字符串文字““Seven””测试了速度,以排除使用常量时的特殊编译器优化。速度结果大致相同:`MyEnum myEnum = Enum.Parse&lt;MyEnum&gt;(myString);`平均值:**42.60 ns**,错误:0.322 ns,StdDev:0.301 ns (2认同)

Dav*_*ney 92

你正在寻找Enum.Parse.

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
Run Code Online (Sandbox Code Playgroud)

  • 使用值代替枚举;SomeEnum 值 = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue"); (5认同)

Jor*_*dan 31

在某个时候,添加了 Parse 的通用版本。对我来说,这是更可取的,因为我不需要“尝试”解析,而且我还希望结果内联而不生成输出变量。

ColorEnum color = Enum.Parse<ColorEnum>("blue");
Run Code Online (Sandbox Code Playgroud)

MS 文档:解析

  • 注意:泛型版本不作为 .NET Framework 的一部分存在。等效语法是`ColorEnum color = (ColorEnum)Enum.Parse(typeof(ColorEnum), "blue");`。 (8认同)

Foy*_*rim 29

您现在可以使用扩展方法:

public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}
Run Code Online (Sandbox Code Playgroud)

您可以通过以下代码调用它们(这里FilterType是一个枚举类型):

FilterType filterType = type.ToEnum<FilterType>();
Run Code Online (Sandbox Code Playgroud)

  • @SollyM内部与否,我仍然是维护和使用我的代码的人:如果我在每个intellisense菜单中都有一个ToEnum,就像你说的那样,PI会很恼火,因为你唯一一次转换为枚举是来自字符串或int,你可以肯定你只需要这两种方法.并且两种方法不仅仅是一种,特别是当它们很小并且具有实用类型时:P (3认同)
  • @SollyM我会说这是一个可怕的想法因为这个扩展方法将适用于*all*对象类型.两种扩展方法,一种用于字符串,一种用于int,在我看来会更干净,更安全. (2认同)

bre*_*dan 19

object Enum.Parse(System.Type enumType, string value, bool ignoreCase);
Run Code Online (Sandbox Code Playgroud)

所以,如果你有一个名为heart的枚举,它将如下所示:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());
Run Code Online (Sandbox Code Playgroud)


Tim*_*imo 18

谨防:

enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}
Run Code Online (Sandbox Code Playgroud)

Enum.(Try)Parse() 接受多个逗号分隔的参数,并将它们与二进制'或'组合|.你不能禁用它,在我看来你几乎从不想要它.

var x = Enum.Parse("One,Two"); // x is now Three
Run Code Online (Sandbox Code Playgroud)

即使Three没有定义,x仍然会得到int值3.更糟糕的是:Enum.Parse()可以为你提供一个甚至没有为枚举定义的值!

我不想让用户自愿或不情愿地触发这种行为.

另外,正如其他人所提到的,对于大型枚举,性能不太理想,即可能值的数量是线性的.

我建议如下:

    public static bool TryParse<T>(string value, out T result)
        where T : struct
    {
        var cacheKey = "Enum_" + typeof(T).FullName;

        // [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
        // [Implementation off-topic.]
        var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);

        return enumDictionary.TryGetValue(value.Trim(), out result);
    }

    private static Dictionary<string, T> CreateEnumDictionary<T>()
    {
        return Enum.GetValues(typeof(T))
            .Cast<T>()
            .ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
    }
Run Code Online (Sandbox Code Playgroud)

  • 事实上,知道"Enum.(Try)Parse接受多个逗号分隔的参数,并将它们与二进制'或'`组合起来非常有用.意味着您可以将枚举值设置为2的幂,并且您有一种非常简单的方法来解析多个布尔标志,例如."UseSSL,NoRetries,同步".事实上,这可能是它的设计目标. (4认同)

tag*_*s2k 16

Enum.Parse是你的朋友:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
Run Code Online (Sandbox Code Playgroud)


Blo*_*mer 15

这里的大多数答案都要求您每次调用扩展方法时始终传递枚举的默认值。如果您不想采用这种方法,可以像下面这样实现:

 public static TEnum ToEnum<TEnum>(this string value) where TEnum : struct
 {
     if (string.IsNullOrWhiteSpace(value))
          return default(TEnum);

     return Enum.TryParse(value, true, out TEnum result) ? result : default(TEnum);

 }
Run Code Online (Sandbox Code Playgroud)

使用默认文字(从 C# 7.1 开始)

 public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue = default) where TEnum : struct
 {
       if (string.IsNullOrWhiteSpace(value))
            return defaultValue ;

       return Enum.TryParse(value, true, out TEnum result) ? result : defaultValue ;

 }
Run Code Online (Sandbox Code Playgroud)

更好的是:

public static TEnum ToEnum<TEnum>(this string value) where TEnum : struct
{
      if (string.IsNullOrWhiteSpace(value))
           return default;

      return Enum.TryParse(value, true, out TEnum result) ? result : default;

}
Run Code Online (Sandbox Code Playgroud)


Nel*_*lly 11

您可以使用默认值扩展接受的答案以避免例外:

public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
    try
    {
        T enumValue;
        if (!Enum.TryParse(value, true, out enumValue))
        {
            return defaultValue;
        }
        return enumValue;
    }
    catch (Exception)
    {
        return defaultValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你称之为:

StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);
Run Code Online (Sandbox Code Playgroud)

  • 什么时候会抛出异常? (3认同)

gap*_*gap 8

我们无法假设完全有效的输入,并且随着@ Keith的回答变化:

public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
    TEnum tmp; 
    if (!Enum.TryParse<TEnum>(value, true, out tmp))
    {
        tmp = new TEnum();
    }
    return tmp;
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*ade 7

// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}
Run Code Online (Sandbox Code Playgroud)


jit*_*.gs 5

将字符串解析为TEnum,不使用try/catch,也不使用.NET 4.5中的TryParse()方法

/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
    enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
    if(!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
        return false;

    enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
    return true;
}
Run Code Online (Sandbox Code Playgroud)


alh*_*hpe 5

我喜欢扩展方法解决方案..

namespace System
{
    public static class StringExtensions
    {

        public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
        {
            T result;

            var isEnum = Enum.TryParse(value, out result);

            output = isEnum ? result : default(T);

            return isEnum;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

下面是我的测试实现。

using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;

private enum Countries
    {
        NorthAmerica,
        Europe,
        Rusia,
        Brasil,
        China,
        Asia,
        Australia
    }

   [TestMethod]
        public void StringExtensions_On_TryParseAsEnum()
        {
            var countryName = "Rusia";

            Countries country;
            var isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsTrue(isCountry);
            AreEqual(Countries.Rusia, country);

            countryName = "Don't exist";

            isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsFalse(isCountry);
            AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
        }
Run Code Online (Sandbox Code Playgroud)


Bri*_*ice 5

使用 TryParse 的超级简单代码:

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;
Run Code Online (Sandbox Code Playgroud)


JCi*_*sar 5

不确定这是什么时候添加的,但在 Enum 类上现在有一个

Parse<TEnum>(stringValue)

像这样使用有问题的例子:

var MyStatus = Enum.Parse<StatusEnum >("Active")

或忽略大小写:

var MyStatus = Enum.Parse<StatusEnum >("active", true)

这是它使用的反编译方法:

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value) where TEnum : struct
    {
      return Enum.Parse<TEnum>(value, false);
    }

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value, bool ignoreCase) where TEnum : struct
    {
      TEnum result;
      Enum.TryParse<TEnum>(value, ignoreCase, true, out result);
      return result;
    }
Run Code Online (Sandbox Code Playgroud)

  • 这是在 .NET Core 2.0 中添加的(我在[其他答案](/sf/answers/4548376031/)中写了一些关于它的内容) (3认同)