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

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

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

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

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

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

1071917 次浏览

你正在寻找解析枚举

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

在. NET Core和. NET Framework≥4.0有一个泛型解析方法中:

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中,因为(没有命名空间控制)它将出现在string的所有实例上,无论它们是否包含枚举(因此1234.ToString().ToEnum(StatusEnum.None)将是有效的但无意义的)。通常最好避免将Microsoft的核心类与仅适用于非常特定上下文的额外方法弄乱,除非您的整个开发团队都非常了解这些扩展的作用。

解析枚举是你的朋友:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

所以,如果你有一个名为情绪的枚举,它看起来像这样:

   enum Mood{Angry,Happy,Sad}
// ...Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);Console.WriteLine("My mood is: {0}", m.ToString());
// str.ToEnum<EnumType>()T static ToEnum<T>(this string str){return (T) Enum.Parse(typeof(T), str);}

请注意,Enum.Parse()的性能并不理想,因为它是通过反射实现的。(Enum.ToString()也是如此,反之亦然。)

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

我们不能假设完全有效的输入,并采用@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;}

从. NET 4.5中解析字符串到TEnum,没有try/cat和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;}

使用#0(≥. NET 4.0):

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

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

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

您现在可以使用扩展方法

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

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

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

如果默认值不是枚举,则Enums. TryParse将失败并抛出被捕获的异常。

经过多年在我们的代码中使用这个函数在许多地方,也许添加这个操作成本性能的信息是很好的!

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

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

博客是枚举-NET 3.5中更好的语法、更高的性能和TryParse

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

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.

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

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}

注意:

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

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

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

即使没有定义Threex仍然会得到int值3。更糟糕的是:Enumm. 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);}

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

    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);}elsedic = dicEnum[t];if (!dic.ContainsKey(value))return defaultValue;elsereturn (T)dic[value];}

我发现这里没有考虑枚举值具有Enum成员值的情况。所以我们开始:

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

这个enum的例子:

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

使用TryParse的超级简单代码:

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

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

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

试试这个样本:

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

        <Extension()>Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnumIf String.IsNullOrEmpty(value) ThenReturn defaultValueEnd If
Return [Enum].Parse(GetType(TEnum), value, True)End Function
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){if (string.IsNullOrEmpty(value))return defaultValue;
return Enum.Parse(typeof(TEnum), value, true);}

不确定这是什么时候添加的,但在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;}

如果属性名称与您想要的名称不同(即语言差异),您可以这样做:

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

如果您想在null或空时使用默认值(例如,当从配置文件中检索并且该值不存在时)并在字符串或数字与任何枚举值不匹配时抛出异常。但要注意Timo的回答中的警告(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;}

首先,你需要装饰你的枚举,像这样:

    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

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

ColorEnum color = Enum.Parse<ColorEnum>("blue");

MS文档:解析

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

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

使用默认文字(可从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 ;
}

更好的是:

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

我开始使用这种方法。性能似乎还可以,但它需要一些样板代码设置。

public enum StatusType {Success,Pending,Rejected}
static class StatusTypeMethods {
public static StatusType GetEnum(string type) {switch (type) {case nameof(StatusType.Success): return StatusType.Success;case nameof(StatusType.Pending): return StatusType.Pending;case nameof(StatusType.Rejected): return StatusType.Rejected;default:throw new ArgumentOutOfRangeException(nameof(type), type, null);};}}

稍后,您可以像这样使用它:

StatusType = StatusType.GetEnum("Success");