我应该如何将string转换为C#中的枚举?
在C#中将string转换为枚举值的最佳方法是什么?
我有一个包含枚举值的HTMLselect标记。 当页面发布时,我想要拿起值(这将是一个string的forms),并将其转换为枚举值。
在理想的世界里,我可以做这样的事情:
StatusEnum MyStatus = StatusEnum.Parse("Active");
但这不是一个有效的代码。
在.NET Core和.NET> 4中有一个通用的parsing方法 :
Enum.TryParse("Active", out StatusEnum myStatus);
这也包括了C#7的新内联variables,所以这样做的尝试parsing,转换为明确的枚举types和初始化+填充myStatus
variables。
如果您有权访问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");
在评论中build议的一个选项是添加一个足够简单的扩展:
public static T ToEnum<T>(this string value) { return (T) Enum.Parse(typeof(T), value, true); } StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();
最后,如果string不能被parsing,你可能想要使用默认的枚举:
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)
会有效但无意义)。 除非整个开发团队都非常了解这些扩展的function,否则通常最好避免使用只适用于特定上下文的额外方法来混淆微软的核心类。
使用Enum.TryParse<T>(String, T)
( Enum.TryParse<T>(String, T)
4.0):
StatusEnum myStatus; Enum.TryParse("Active", out myStatus);
使用C#7.0的参数types内联可以进一步简化:
Enum.TryParse("Active", out StatusEnum myStatus);
请注意,Enum.Parse()的性能很糟糕,因为它是通过reflection来实现的。 (Enum.ToString也是如此,反之亦然。)
如果您需要将string转换为对性能敏感的代码中的枚举,最好的方法是在启动时创build一个Dictionary<String,YourEnum>
,并使用它来完成转换。
你正在寻找Enum.Parse 。
SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
您现在可以使用扩展方法 :
public static T ToEnum<T>(this string value, bool ignoreCase = true) { return (T) Enum.Parse(typeof (T), value, ignoreCase); }
你可以通过下面的代码来调用它们(在这里, FilterType
是一个枚举types):
FilterType filterType = type.ToEnum<FilterType>();
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());
Enum.Parse是你的朋友:
StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
您可以将接受的答案扩展为默认值以避免例外:
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);
我们不能假设完全有效的input,并且用@ 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; }
// str.ToEnum<EnumType>() T static ToEnum<T>(this string str) { return (T) Enum.Parse(typeof(T), str); }
谨防:
enum Example { One = 1, Two = 2, Three = 3 }
Enum.(Try)Parse()
接受多个以逗号分隔的参数,并将它们与二进制“或”相结合 。 你不能禁用这个,在我看来你几乎不需要它。
var x = Enum.Parse("One,Two"); // x is now Three
即使Three
没有定义, x
仍然会得到int值3
。 这更糟糕:Enum.Parse()可以给你一个甚至没有为枚举定义的值!
我不愿意经历用户的后果,不情愿地或不情愿地触发这种行为。
此外,正如其他人所提到的那样,对于大枚举来说,性能并不理想,即可能值的数量是线性的。
我build议如下:
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); }
将stringparsing为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; }
超级简单的代码使用TryParse:
var value = "Active"; StatusEnum status; if (!Enum.TryParse<StatusEnum>(value, out status)) status = StatusEnum.Unknown;
我喜欢扩展方法解决scheme..
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; } } }
这里下面我的testing执行。
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 }
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.
我使用类(Enum的强types版本parsing和性能改进)。 我发现它在GitHub上,它也应该为.NET 3.5工作。 它有一些内存开销,因为它缓冲一个字典。
StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");
blogpost是枚举 – 更好的语法,改进的性能和TryParse NET 3.5中 。
和代码: https : //github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs
为了performance,这可能有所帮助
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值的情况没有考虑。 所以在这里我们去:
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 }
您必须使用Enum.Parse从Enum中获取对象值,之后您必须将对象值更改为特定的枚举值。 铸造枚举值可以通过使用Convert.ChangeType。 请看下面的代码片段
public T ConvertStringValueToEnum<T>(string valueToParse){ return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T)); }