将字符串转换为C#中的枚举

Convert a string to an enum in C#

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

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

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

1
StatusEnum MyStatus = StatusEnum.Parse("Active");

但这不是一个有效的代码。


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

1
Enum.TryParse("Active", out StatusEnum myStatus);

这还包括C_7的新的内联out变量,因此这将进行try解析、转换为显式枚举类型并初始化+填充myStatus变量。

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

原始答案

在.NET中,它相当难看(直到4或更高版本):

1
StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum),"Active", true);

我倾向于简化这一点:

1
2
3
4
public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

然后我可以做:

1
StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

注释中建议的一个选项是添加一个扩展名,这非常简单:

1
2
3
4
5
6
public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus ="Active".ToEnum<StatusEnum>();

最后,如果无法分析字符串,则可能需要使用默认枚举:

1
2
3
4
5
6
7
8
9
10
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;
}

这就是电话:

1
StatusEnum MyStatus ="Active".ToEnum(StatusEnum.None);

但是,我会小心地将这样的扩展方法添加到string中,因为(没有名称空间控制)它将出现在string的所有实例上,无论它们是否持有枚举(因此1234.ToString().ToEnum(StatusEnum.None)是有效的,但是无意义的)。通常最好避免用只在非常特定的环境中应用的额外方法来混乱微软的核心类,除非你的整个开发团队对这些扩展有很好的理解。


使用Enum.TryParse(String, T)(≥.net 4.0):

1
2
StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);

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

1
Enum.TryParse("Active", out StatusEnum myStatus);


请注意,enum.parse()的性能很糟糕,因为它是通过反射实现的。(Enum.ToString也是如此,反过来也是如此。)

如果需要在性能敏感的代码中将字符串转换为枚举,最好是在启动时创建一个Dictionary,并使用它进行转换。


您正在查找Enum.Parse。

1
SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum),"EnumValue");


现在可以使用扩展方法:

1
2
3
4
public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}

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

1
FilterType filterType = type.ToEnum<FilterType>();


当心:

1
2
3
4
5
6
enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}

Enum.(Try)Parse()接受多个逗号分隔的参数,并将它们与binary'或'|组合在一起。你不能使它失效,在我看来你几乎不想要它。

1
var x = Enum.Parse("One,Two"); // x is now Three

即使没有定义Threex仍然可以得到int值3。更糟糕的是:enum.parse()可以为您提供一个甚至没有为枚举定义的值!

我不想体验用户自愿或不愿意引发这种行为的后果。

另外,正如其他人所提到的,对于大型枚举(即可能值的线性数)来说,性能并不理想。

我建议如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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);
    }


1
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

因此,如果您有一个名为mood的枚举,它将如下所示:

1
2
3
4
5
6
7
8
9
10
   enum Mood
   {
      Angry,
      Happy,
      Sad
   }

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


Enum.Parse是您的朋友:

1
StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum),"Active");

您可以使用默认值扩展接受的答案,以避免出现异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
    }
}

然后你这样称呼它:

1
StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);


我们不能假设输入是完全有效的,并使用了@keith's答案的这种变体:

1
2
3
4
5
6
7
8
9
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;
}

1
2
3
4
5
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str)
{
    return (T) Enum.Parse(typeof(T), str);
}

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

1
2
3
4
5
6
7
8
9
10
11
12
/// <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;
}


使用胰蛋白酶的超简单代码:

1
2
3
4
5
var value ="Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;

我喜欢扩展方法的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
        }
    }
}

下面是我的测试实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
        }


1
2
3
4
5
6
public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call

=======一个完整的程序==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;

class Program
{
    enum PetType
    {
    None,
    Cat = 1,
    Dog = 2
    }

    static void Main()
    {

    // Possible user input:
    string value ="Dog";

    // Try to convert the string to an enum:
    PetType pet = (PetType)Enum.Parse(typeof(PetType), value);

    // See if the conversion succeeded:
    if (pet == PetType.Dog)
    {
        Console.WriteLine("Equals dog.");
    }
    }
}
-------------
Output

Equals dog.

我使用了类(具有解析和性能改进的强类型枚举版本)。我在Github上找到的,它也应该适用于.NET 3.5。它有一些内存开销,因为它缓冲字典。

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

blogpost是Enums——更好的语法、改进的性能和net 3.5中的Tryparse。

代码:https://github.com/damieng/damiengkit/blob/master/csharp/damieng.library/system/enumt.cs


尝试此示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 public static T GetEnum<T>(string model)
    {
        var newModel = GetStringForEnum(model);

        if (!Enum.IsDefined(typeof(T), newModel))
        {
            return (T)Enum.Parse(typeof(T),"None", true);
        }

        return (T)Enum.Parse(typeof(T), newModel.Result, true);
    }

    private static Task<string> GetStringForEnum(string model)
    {
        return Task.Run(() =>
        {
            Regex rgx = new Regex("[^a-zA-Z0-9 -]");
            var nonAlphanumericData = rgx.Matches(model);
            if (nonAlphanumericData.Count < 1)
            {
                return model;
            }
            foreach (var item in nonAlphanumericData)
            {
                model = model.Replace((string)item,"");
            }
            return model;
        });
    }

在这个示例中,您可以发送每个字符串,并设置您的Enum。如果您的Enum有您想要的数据,则将其作为您的Enum类型返回。


对于性能而言,这可能有助于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
    public static T ToEnum<T>(this string value, T defaultValue)
    {
        var t = typeof(T);
        Dictionary<string, object> dic;
        if (!dicEnum.ContainsKey(t))
        {
            dic = new Dictionary<string, object>();
            dicEnum.Add(t, dic);
            foreach (var en in Enum.GetValues(t))
                dic.Add(en.ToString(), en);
        }
        else
            dic = dicEnum[t];
        if (!dic.ContainsKey(value))
            return defaultValue;
        else
            return (T)dic[value];
    }

我发现这里不考虑枚举值为EnumMember值的情况。所以我们走到这里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Runtime.Serialization;

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

    TEnum result;
    var enumType = typeof(TEnum);
    foreach (var enumName in Enum.GetNames(enumType))
    {
        var fieldInfo = enumType.GetField(enumName);
        var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
        if (enumMemberAttribute?.Value == value)
        {
            return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
        }
    }

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

枚举示例:

1
2
3
4
5
6
7
8
9
public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value ="OPEN MIGRATE")]
    OpenMigrate = 4
}


必须使用Enum.Parse从Enum中获取对象值,然后必须将对象值更改为特定的Enum值。可以使用convert.changeType来强制转换为枚举值。请看下面的代码段

1
2
3
public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}