我想编写一个函数,它可以验证给定值(作为字符串传递)与enum
的可能值。在匹配的情况下,它应该返回枚举实例;否则,它应该返回一个默认值。
该函数可能不会在内部使用try
/ catch
,这会排除使用Enum.Parse
,这会在给定无效参数时抛出异常。
我想使用TryParse
函数的内容来实现这一点:
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
object enumValue;
if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
{
return defaultValue;
}
return (TEnum) enumValue;
}
答案 0 :(得分:99)
Enum.IsDefined将完成任务。它可能不像TryParse那样有效,但它可以在没有异常处理的情况下工作。
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
return defaultValue;
return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}
值得注意的是:在.NET 4.0中添加了TryParse
方法。
答案 1 :(得分:31)
正如其他人所说,你必须实现自己的TryParse
。 Simon Mourier正在提供一个完整的实现,它可以处理所有事情。
如果使用位域枚举(即标志),则还必须处理类似"MyEnum.Val1|MyEnum.Val2"
的字符串,它是两个枚举值的组合。如果你只是用这个字符串调用Enum.IsDefined
,它将返回false,即使Enum.Parse
正确处理它。
<强>更新强>
正如Lisa和Christian在评论中所提到的,Enum.TryParse
现在可用于.NET4及更高版本的C#。
MSDN Docs
答案 2 :(得分:19)
以下是EnumTryParse
的自定义实现。与其他常见实现不同,它还支持使用Flags
属性标记的枚举。
/// <summary>
/// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
/// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
/// </summary>
/// <param name="type">The enum target type. May not be null.</param>
/// <param name="input">The input text. May be null.</param>
/// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
/// <returns>
/// true if s was converted successfully; otherwise, false.
/// </returns>
public static bool EnumTryParse(Type type, string input, out object value)
{
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsEnum)
throw new ArgumentException(null, "type");
if (input == null)
{
value = Activator.CreateInstance(type);
return false;
}
input = input.Trim();
if (input.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
string[] names = Enum.GetNames(type);
if (names.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
Type underlyingType = Enum.GetUnderlyingType(type);
Array values = Enum.GetValues(type);
// some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
return EnumToObject(type, underlyingType, names, values, input, out value);
// multi value enum
string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
ulong ul = 0;
foreach (string tok in tokens)
{
string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
if (token.Length == 0)
continue;
object tokenValue;
if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
{
value = Activator.CreateInstance(type);
return false;
}
ulong tokenUl;
switch (Convert.GetTypeCode(tokenValue))
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
break;
//case TypeCode.Byte:
//case TypeCode.UInt16:
//case TypeCode.UInt32:
//case TypeCode.UInt64:
default:
tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
break;
}
ul |= tokenUl;
}
value = Enum.ToObject(type, ul);
return true;
}
private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };
private static object EnumToObject(Type underlyingType, string input)
{
if (underlyingType == typeof(int))
{
int s;
if (int.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(uint))
{
uint s;
if (uint.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(ulong))
{
ulong s;
if (ulong.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(long))
{
long s;
if (long.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(short))
{
short s;
if (short.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(ushort))
{
ushort s;
if (ushort.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(byte))
{
byte s;
if (byte.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(sbyte))
{
sbyte s;
if (sbyte.TryParse(input, out s))
return s;
}
return null;
}
private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
{
for (int i = 0; i < names.Length; i++)
{
if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
{
value = values.GetValue(i);
return true;
}
}
if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
{
object obj = EnumToObject(underlyingType, input);
if (obj == null)
{
value = Activator.CreateInstance(type);
return false;
}
value = obj;
return true;
}
value = Activator.CreateInstance(type);
return false;
}
答案 3 :(得分:16)
最后,您必须围绕Enum.GetNames
:
public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
// Can't make this a type constraint...
if (!typeof(T).IsEnum) {
throw new ArgumentException("Type parameter must be an enum");
}
var names = Enum.GetNames(typeof(T));
value = (Enum.GetValues(typeof(T)) as T[])[0]; // For want of a better default
foreach (var name in names) {
if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
value = (T)Enum.Parse(typeof(T), name);
return true;
}
}
return false;
}
附加说明:
Enum.TryParse
包含在.NET 4中。请参阅此处http://msdn.microsoft.com/library/dd991876(VS.100).aspx Enum.Parse
捕获失败时抛出的异常。找到匹配项时可能会更快,但如果没有,则可能会更慢。根据您正在处理的数据,这可能是也可能不是净改善。答案 4 :(得分:8)
基于.NET 4.5
以下示例代码
using System;
enum Importance
{
None,
Low,
Medium,
Critical
}
class Program
{
static void Main()
{
// The input value.
string value = "Medium";
// An unitialized variable.
Importance importance;
// Call Enum.TryParse method.
if (Enum.TryParse(value, out importance))
{
// We now have an enum type.
Console.WriteLine(importance == Importance.Medium);
}
}
}
答案 5 :(得分:4)
我有一个可以在UnconstrainedMelody中使用的优化实现。实际上,它只是缓存了名称列表,但它是以一种不错的,强类型的,通用约束的方式实现的:)
答案 6 :(得分:3)
enum EnumStatus
{
NAO_INFORMADO = 0,
ENCONTRADO = 1,
BLOQUEADA_PELO_ENTREGADOR = 2,
DISPOSITIVO_DESABILITADO = 3,
ERRO_INTERNO = 4,
AGARDANDO = 5
}
...
if (Enum.TryParse<EnumStatus>(item.status, out status)) {
}
答案 7 :(得分:2)
目前没有开箱即用的Enum.TryParse。这已在Connect(Still no Enum.TryParse)上请求,并得到一个响应,表明可能包含在.NET 3.5之后的下一个框架中。您现在必须实施建议的解决方法。
答案 8 :(得分:1)
避免异常处理的唯一方法是使用GetNames()方法,我们都知道异常不应该被滥用于常见的应用程序逻辑:)
答案 9 :(得分:1)
缓存动态生成的函数/字典是否允许?
因为你没有(似乎)提前知道枚举的类型,所以第一次执行可以生成后续执行可以利用的东西。
您甚至可以缓存Enum.GetNames()
的结果您是否尝试针对CPU或内存进行优化?你真的需要吗?
答案 10 :(得分:0)
正如其他人已经说过的,如果你不使用Try&amp; Catch,你需要使用IsDefined或GetNames ...... 这里有一些样本......它们基本上都是一样的,第一个处理可以为空的枚举。我更喜欢第二个,因为它是字符串的扩展,而不是枚举...但你可以根据需要混合它们!
答案 11 :(得分:0)
没有TryParse,因为Enum的类型直到运行时才知道。 TryParse遵循与Date.TryParse方法相同的方法,会在ByRef参数上抛出隐式转换错误。
我建议做这样的事情:
//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);
//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
if (Enum.IsDefined(enumType, value)) {
return Enum.Parse(enumType, value);
} else {
return Enum.Parse(enumType, NotDefinedReplacement);
}
}
答案 12 :(得分:-1)
看一下Enum类(struct?)本身。有一个Parse方法,但我不确定tryparse。
答案 13 :(得分:-2)
此方法将转换枚举类型:
public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
{
if (!Enum.IsDefined(typeof(TEnum), EnumValue))
{
Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
if ( EnumValue.GetType() == enumType )
{
string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
if( name != null)
return (TEnum)Enum.Parse(typeof(TEnum), name);
return defaultValue;
}
}
return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
}
它检查基础类型并获取要解析的名称。如果一切都失败了,它将返回默认值。