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

时间:2008-08-19 12:51:55

标签: c# string enums

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

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

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

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

但这不是有效的代码。

26 个答案:

答案 0 :(得分:1200)

在.NET Core和.NET> 4 there is a generic parse method中:

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

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

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

原始答案

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

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

我倾向于用以下方式简化:

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

然后我可以做:

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

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

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

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

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

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

这就是电话:

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

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

答案 1 :(得分:283)

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

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

使用C#7.0的parameter type inlining

可以进一步简化它
Enum.TryParse("Active", out StatusEnum myStatus);

答案 2 :(得分:175)

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

如果您需要在性能敏感的代码中将字符串转换为枚举,最好的办法是在启动时创建Dictionary<String,YourEnum>并使用它来进行转换。

答案 3 :(得分:82)

您正在寻找Enum.Parse

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

答案 4 :(得分:26)

您现在可以使用extension methods

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

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

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

答案 5 :(得分:16)

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

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

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

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

答案 6 :(得分:16)

<强>要注意:

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

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

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

即使未定义Threex仍会获得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);
    }

答案 7 :(得分:15)

Enum.Parse是你的朋友:

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

答案 8 :(得分:12)

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

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

然后你称之为:

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

答案 9 :(得分:9)

我们无法假设完全有效的输入,并且使用@ 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;
}

答案 10 :(得分:7)

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

答案 11 :(得分: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;
}

答案 12 :(得分:3)

使用TryParse的超简单代码:

var value = "Active";

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

答案 13 :(得分:2)

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

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

下面是我的测试实现。

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
        }

答案 14 :(得分:1)

public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

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

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

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.

答案 15 :(得分:1)

对于表现,这可能会有所帮助:

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

答案 16 :(得分:1)

我发现这里没有考虑具有EnumMember值的枚举值的情况。所以我们走了:

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

这个枚举的例子:

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

答案 17 :(得分:1)

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

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

博客文章是 Enums – Better syntax, improved performance and TryParse in NET 3.5

代码: https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs

答案 18 :(得分:1)

您必须使用Enum.Parse从Enum获取对象值,之后您必须将对象值更改为特定的枚举值。可以使用Convert.ChangeType来转换为枚举值。请查看以下代码段

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

答案 19 :(得分:1)

尝试此示例:

 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类型返回。

答案 20 :(得分:1)

不确定何时添加,但是在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;
    }

答案 21 :(得分:1)

如果属性名称与您要调用的名称不同(即语言不同),则可以执行以下操作:

MyType.cs

using System;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public enum MyType
{
    [EnumMember(Value = "person")]
    Person,
    [EnumMember(Value = "annan_deltagare")]
    OtherPerson,
    [EnumMember(Value = "regel")]
    Rule,
}

EnumExtensions.cs

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public static class EnumExtensions
{
    public static TEnum ToEnum<TEnum>(this string value) where TEnum : Enum
    {
        var jsonString = $"'{value.ToLower()}'";
        return JsonConvert.DeserializeObject<TEnum>(jsonString, new StringEnumConverter());
    }

    public static bool EqualsTo<TEnum>(this string strA, TEnum enumB) where TEnum : Enum
    {
        TEnum enumA;
        try
        {
            enumA = strA.ToEnum<TEnum>();
        }
        catch
        {
            return false;
        }
        return enumA.Equals(enumB);
    }
}

Program.cs

public class Program
{
    static public void Main(String[] args) 
    { 
        var myString = "annan_deltagare";
        var myType = myString.ToEnum<MyType>();
        var isEqual = myString.EqualsTo(MyType.OtherPerson);
        //Output: true
    }     
}

答案 22 :(得分:0)

        <Extension()>
    Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
        If String.IsNullOrEmpty(value) Then
            Return defaultValue
        End If

        Return [Enum].Parse(GetType(TEnum), value, True)
    End Function

答案 23 :(得分:0)

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

return Enum.Parse(typeof(TEnum), value, true);}

答案 24 :(得分:0)

如果要在null或空值时使用默认值(例如,从配置文件中检索并且该值不存在时),并且在字符串或数字与任何枚举值都不匹配时引发异常。不过请提防蒂莫(https://stackoverflow.com/a/34267134/2454604)的警告。

    public static T ParseEnum<T>(this string s, T defaultValue, bool ignoreCase = false) 
        where T : struct, IComparable, IConvertible, IFormattable//If C# >=7.3: struct, System.Enum 
    {
        if ((s?.Length ?? 0) == 0)
        {
            return defaultValue;
        }

        var valid = Enum.TryParse<T>(s, ignoreCase, out T res);

        if (!valid || !Enum.IsDefined(typeof(T), res))
        {
            throw new InvalidOperationException(
                $"'{s}' is not a valid value of enum '{typeof(T).FullName}'!");
        }
        return res;
    }

答案 25 :(得分:-1)

首先,您需要装饰您的枚举,如下所示:

    public enum Store : short
{
    [Description("Rio Big Store")]
    Rio = 1
}

在 .net 5 中,我创建了这个扩展方法:

//The class also needs to be static, ok?
public static string GetDescription(this System.Enum enumValue)
    {
        FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString());

        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(
            typeof(DescriptionAttribute), false);

        if (attributes != null && attributes.Length > 0) return attributes[0].Description;
        else return enumValue.ToString();
    }

现在你有一个可以在任何枚举中使用的扩展方法

像这样:

var Desc = Store.Rio.GetDescription(); //Store is your Enum