Enum.(Try)Parse()accepts multiple, comma-separated arguments, and combines them with binary 'or' | . 你不能禁用它,在我看来你几乎从不想要它 .
var x = Enum.Parse("One,Two"); // x is now Three
即使未定义 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);
}
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
}
5
对于性能,这可能有所帮助
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];
}
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;
}
但是,我会小心地将这样的扩展方法添加到 string 中(没有命名空间控制)它将出现在 string 的所有实例上,无论它们是否包含枚举(因此 1234.ToString().ToEnum(StatusEnum.None) 将是有效但无意义的) . 它是's often be best to avoid cluttering Microsoft'的核心类,其中包含仅适用于非常特定的上下文的额外方法,除非您的整个开发团队非常了解这些扩展的作用 .
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.
var value = "Active";
StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
status = StatusEnum.Unknown;
23
我发现这里没有考虑具有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
}
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;
}
}
20 回答
BEWARE:
Enum.(Try)Parse()
accepts multiple, comma-separated arguments, and combines them with binary 'or' | . 你不能禁用它,在我看来你几乎从不想要它 .即使未定义
Three
,x
仍将获得int值3
. 更糟糕的是:Enum.Parse()可以为你提供一个甚至没有为枚举定义的值!我不想让用户自愿或不情愿地触发这种行为 .
另外,正如其他人所提到的,对于大型枚举,性能不太理想,即可能值的数量是线性的 .
我建议如下:
在没有try / catch和.NET 4.5的TryParse()方法的情况下,将字符串解析为TEnum
请注意,Enum.Parse()的性能很糟糕,因为它是通过反射实现的 . (对于Enum.ToString也是如此,这是另一种方式 . )
如果您需要在性能敏感的代码中将字符串转换为枚举,最好的办法是在启动时创建
Dictionary<String,YourEnum>
并使用它来进行转换 .使用Enum.TryParse<T>(String, T)(≥.NET4.0):
使用C#7.0的parameter type inlining可以进一步简化它:
我喜欢扩展方法解决方案..
下面是我的测试实现 .
对于性能,这可能有所帮助
所以,如果你有一个名为heart的枚举,它将如下所示:
你在找Enum.Parse .
在.NET Core和.NET中> 4 there is a generic parse method:
这还包括C#7的新内联
out
变量,因此这将执行try-parse,转换为显式枚举类型并初始化填充myStatus
变量 .如果您可以访问C#7和最新的.NET,这是最好的方法 .
原始答案
在.NET中它相当丑陋(直到4或以上):
我倾向于简化这个:
然后我可以做:
评论中建议的一个选项是添加一个扩展,这很简单:
最后,如果无法解析字符串,您可能希望使用默认枚举:
这使得这个电话:
但是,我会小心地将这样的扩展方法添加到
string
中(没有命名空间控制)它将出现在string
的所有实例上,无论它们是否包含枚举(因此1234.ToString().ToEnum(StatusEnum.None)
将是有效但无意义的) . 它是's often be best to avoid cluttering Microsoft'的核心类,其中包含仅适用于非常特定的上下文的额外方法,除非您的整个开发团队非常了解这些扩展的作用 .您现在可以使用extension methods:
您可以通过以下代码调用它们(此处,
FilterType
是枚举类型):====================一个完整的程序====================
Enum.Parse是你的朋友:
使用TryParse的超级简单代码:
我发现这里没有考虑具有EnumMember值的枚举值的情况 . 所以我们走了:
这个枚举的例子:
您可以使用默认值扩展接受的答案以避免例外:
然后你称之为:
我使用了类(Enum的强类型版本,具有解析和性能改进) . 我在GitHub上找到它,它也适用于.NET 3.5 . 它有一些内存开销,因为它缓冲字典 .
博文是Enums – Better syntax, improved performance and TryParse in NET 3.5 .
和代码:https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs
我们无法假设完全有效的输入,并且随着@ Keith的回答变化:
您必须使用Enum.Parse从Enum获取对象值,之后您必须将对象值更改为特定的枚举值 . 可以使用Convert.ChangeType来转换为枚举值 . 请查看以下代码段
试试这个样本:
在此示例中,您可以发送每个字符串,并设置
Enum
. 如果Enum
拥有您想要的数据,请将其作为Enum
类型返回 .