C# 验证枚举值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Validate Enum Values
提问by Jedi Master Spooky
I need to validate an integer to know if is a valid enum value.
我需要验证一个整数以知道它是否是一个有效的枚举值。
What is the best way to do this in C#?
在 C# 中执行此操作的最佳方法是什么?
采纳答案by Vman
You got to love these folk who assume that data not only always comes from a UI, but a UI within your control!
您一定会喜欢这些人,他们认为数据不仅总是来自 UI,而且来自您可以控制的 UI!
IsDefined
is fine for most scenarios, you could start with:
IsDefined
对于大多数情况都可以,您可以从以下内容开始:
public static bool TryParseEnum<TEnum>(this int enumValue, out TEnum retVal)
{
retVal = default(TEnum);
bool success = Enum.IsDefined(typeof(TEnum), enumValue);
if (success)
{
retVal = (TEnum)Enum.ToObject(typeof(TEnum), enumValue);
}
return success;
}
(Obviously just drop the ‘this' if you don't think it's a suitable int extension)
(显然,如果您认为它不是合适的 int 扩展名,请删除“this”)
回答by Jon Limjap
Brad Abrams specifically warns against Enum.IsDefined
in his post The Danger of Oversimplification.
布拉德·艾布拉姆斯(Brad Abrams)Enum.IsDefined
在他的帖子“过度简化的危险”中特别警告。
The best way to get rid of this requirement (that is, the need to validate enums) is to remove ways where users can get it wrong, e.g., an input box of some sort. Use enums with drop downs, for example, to enforce only valid enums.
摆脱这种需求(即需要验证枚举)的最佳方法是删除用户可能出错的方式,例如某种输入框。例如,使用带有下拉菜单的枚举来仅强制执行有效的枚举。
回答by Mike Polen
回答by deegee
IMHO the post marked as the answer is incorrect.
Parameter and data validation is one of the things that was drilled into me decades ago.
恕我直言,标记为答案的帖子不正确。
参数和数据验证是我几十年前深入研究的内容之一。
WHY
为什么
Validation is required because essentially any integer value can be assigned to an enum without throwing an error.
I spent many days researching C# enum validation because it is a necessary function in many cases.
验证是必需的,因为基本上任何整数值都可以分配给枚举而不会引发错误。
我花了很多天研究 C# 枚举验证,因为它在很多情况下都是必要的功能。
WHERE
在哪里
The main purpose in enum validation for me is in validating data read from a file: you never know if the file has been corrupted, or was modified externally, or was hacked on purpose.
And with enum validation of application data pasted from the clipboard: you never know if the user has edited the clipboard contents.
对我来说,枚举验证的主要目的是验证从文件中读取的数据:您永远不知道文件是否已损坏、是否被外部修改或被故意黑客攻击。
并且对从剪贴板粘贴的应用程序数据进行枚举验证:您永远不知道用户是否编辑了剪贴板内容。
That said, I spent days researching and testing many methods including profiling the performance of every method I could find or design.
也就是说,我花了几天时间研究和测试许多方法,包括分析我能找到或设计的每种方法的性能。
Making calls into anything in System.Enum is so slow that it was a noticeable performance penalty on functions that contained hundreds or thousands of objects that had one or more enums in their properties that had to be validated for bounds.
对 System.Enum 中的任何内容进行调用都非常慢,以至于对于包含成百上千个对象的函数来说,这是一个明显的性能损失,这些对象的属性中具有一个或多个枚举,必须验证边界。
Bottom line, stay away from everythingin the System.Enum class when validating enum values, it is dreadfully slow.
最重要的是,在验证枚举值时远离System.Enum 类中的所有内容,它非常慢。
RESULT
结果
The method that I currently use for enum validation will probably draw rolling eyes from many programmers here, but it is imho the least evil for my specific application design.
我目前用于枚举验证的方法可能会在这里引起许多程序员的翻白眼,但恕我直言,它对我的特定应用程序设计来说是最不邪恶的。
I define one or two constants that are the upper and (optionally) lower bounds of the enum, and use them in a pair of if() statements for validation.
One downside is that you must be sure to update the constants if you change the enum.
This method also only works if the enum is an "auto" style where each enum element is an incremental integer value such as 0,1,2,3,4,.... It won't work properly with Flags or enums that have values that are not incremental.
我定义了一个或两个常量作为枚举的上限和(可选)下限,并在一对 if() 语句中使用它们进行验证。
一个缺点是,如果您更改枚举,您必须确保更新常量。
此方法也仅在枚举是“自动”样式时才有效,其中每个枚举元素都是增量整数值,例如 0,1,2,3,4,...。它不能与标志或枚举一起正常工作具有非增量值。
Also note that this method is almost as fast as regular if "<" ">" on regular int32s (which scored 38,000 ticks on my tests).
另请注意,如果在常规 int32s 上使用 "<" ">"(在我的测试中得分为 38,000 滴答),则此方法几乎与常规方法一样快。
For example:
例如:
public const MyEnum MYENUM_MINIMUM = MyEnum.One;
public const MyEnum MYENUM_MAXIMUM = MyEnum.Four;
public enum MyEnum
{
One,
Two,
Three,
Four
};
public static MyEnum Validate(MyEnum value)
{
if (value < MYENUM_MINIMUM) { return MYENUM_MINIMUM; }
if (value > MYENUM_MAXIMUM) { return MYENUM_MAXIMUM; }
return value;
}
PERFORMANCE
表现
For those who are interested, I profiled the following variations on an enum validation, and here are the results.
对于那些感兴趣的人,我在枚举验证中分析了以下变体,这是结果。
The profiling was performed on release compile in a loop of one million times on each method with a random integer input value. Each test was ran more than 10 times and averaged. The tick results include the total time to execute which will include the random number generation etc. but those will be constant across the tests. 1 tick = 10ns.
分析是在每个方法上使用随机整数输入值在 100 万次循环中在发布编译时执行的。每个测试运行超过 10 次并取平均值。滴答结果包括执行的总时间,其中将包括随机数生成等,但这些在测试中将保持不变。1 滴答 = 10ns。
Note that the code here isn't the complete test code, it is only the basic enum validation method. There were also a lot of additional variations on these that were tested, and all of them with results similar to those shown here that benched 1,800,000 ticks.
注意这里的代码不是完整的测试代码,它只是基本的枚举验证方法。还有很多其他经过测试的变体,所有这些变体的结果都与此处显示的 1,800,000 个滴答声相似。
Listed slowest to fastest with rounded results, hopefully no typos.
以四舍五入的结果列出最慢到最快的结果,希望没有错别字。
Bounds determined in Method= 13,600,000 ticks
方法中确定的界限= 13,600,000 个滴答声
public static T Clamp<T>(T value)
{
int minimum = Enum.GetValues(typeof(T)).GetLowerBound(0);
int maximum = Enum.GetValues(typeof(T)).GetUpperBound(0);
if (Convert.ToInt32(value) < minimum) { return (T)Enum.ToObject(typeof(T), minimum); }
if (Convert.ToInt32(value) > maximum) { return (T)Enum.ToObject(typeof(T), maximum); }
return value;
}
Enum.IsDefined= 1,800,000 ticks
Note: this code version doesn't clamp to Min/Max but returns Default if out of bounds.
Enum.IsDefined= 1,800,000 ticks
注意:此代码版本不限制为最小值/最大值,但如果超出范围则返回默认值。
public static T ValidateItem<T>(T eEnumItem)
{
if (Enum.IsDefined(typeof(T), eEnumItem) == true)
return eEnumItem;
else
return default(T);
}
System.Enum Convert Int32 with casts= 1,800,000 ticks
System.Enum Convert Int32 with casts= 1,800,000 ticks
public static Enum Clamp(this Enum value, Enum minimum, Enum maximum)
{
if (Convert.ToInt32(value) < Convert.ToInt32(minimum)) { return minimum; }
if (Convert.ToInt32(value) > Convert.ToInt32(maximum)) { return maximum; }
return value;
}
if() Min/Max Constants= 43,000 ticks = the winner by 42x and 316x faster.
if() Min/Max Constants= 43,000 ticks = 赢家快 42 倍和 316 倍。
public static MyEnum Clamp(MyEnum value)
{
if (value < MYENUM_MINIMUM) { return MYENUM_MINIMUM; }
if (value > MYENUM_MAXIMUM) { return MYENUM_MAXIMUM; }
return value;
}
-eol-
-eol-
回答by Schultz9999
This is how I do it based on multiple posts online. The reason for doing this is to make sure enums marked with Flags
attribute can also be successfully validated.
这就是我根据在线多个帖子所做的。这样做的原因是为了确保标记有Flags
属性的枚举也可以成功验证。
public static TEnum ParseEnum<TEnum>(string valueString, string parameterName = null)
{
var parsed = (TEnum)Enum.Parse(typeof(TEnum), valueString, true);
decimal d;
if (!decimal.TryParse(parsed.ToString(), out d))
{
return parsed;
}
if (!string.IsNullOrEmpty(parameterName))
{
throw new ArgumentException(string.Format("Bad parameter value. Name: {0}, value: {1}", parameterName, valueString), parameterName);
}
else
{
throw new ArgumentException("Bad value. Value: " + valueString);
}
}
回答by Vman
This answer is in response to deegee's answer which raises the performance issues of System.Enum so should not be taken as my preferred generic answer, more addressing enum validation in tight performance scenarios.
这个答案是对 deegee 的回答的回应,它提出了 System.Enum 的性能问题,因此不应被视为我首选的通用答案,更多地解决了严格性能场景中的枚举验证。
If you have a mission critical performance issue where slow but functional code is being run in a tight loop then I personally would look at moving that code out of the loop if possible instead of solving by reducing functionality. Constraining the code to only support contiguous enums could be a nightmare to find a bug if, for example, somebody in the future decides to deprecate some enum values. Simplistically you could just call Enum.GetValues once, right at the start to avoid triggering all the reflection, etc thousands of times. That should give you an immediate performance increase. If you need more performance and you know that a lot of your enums are contiguous (but you still want to support 'gappy' enums) you could go a stage further and do something like:
如果您有一个关键任务性能问题,其中缓慢但功能性代码在紧密循环中运行,那么我个人会考虑尽可能将该代码移出循环,而不是通过减少功能来解决。例如,如果将来有人决定弃用某些枚举值,那么将代码限制为仅支持连续枚举可能是发现错误的噩梦。简单地说,您可以在开始时调用 Enum.GetValues 一次,以避免触发所有反射等数千次。这应该会立即提高您的性能。如果您需要更高的性能并且您知道您的许多枚举是连续的(但您仍然希望支持“gappy”枚举),您可以更进一步并执行以下操作:
public abstract class EnumValidator<TEnum> where TEnum : struct, IConvertible
{
protected static bool IsContiguous
{
get
{
int[] enumVals = Enum.GetValues(typeof(TEnum)).Cast<int>().ToArray();
int lowest = enumVals.OrderBy(i => i).First();
int highest = enumVals.OrderByDescending(i => i).First();
return !Enumerable.Range(lowest, highest).Except(enumVals).Any();
}
}
public static EnumValidator<TEnum> Create()
{
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException("Please use an enum!");
}
return IsContiguous ? (EnumValidator<TEnum>)new ContiguousEnumValidator<TEnum>() : new JumbledEnumValidator<TEnum>();
}
public abstract bool IsValid(int value);
}
public class JumbledEnumValidator<TEnum> : EnumValidator<TEnum> where TEnum : struct, IConvertible
{
private readonly int[] _values;
public JumbledEnumValidator()
{
_values = Enum.GetValues(typeof (TEnum)).Cast<int>().ToArray();
}
public override bool IsValid(int value)
{
return _values.Contains(value);
}
}
public class ContiguousEnumValidator<TEnum> : EnumValidator<TEnum> where TEnum : struct, IConvertible
{
private readonly int _highest;
private readonly int _lowest;
public ContiguousEnumValidator()
{
List<int> enumVals = Enum.GetValues(typeof (TEnum)).Cast<int>().ToList();
_lowest = enumVals.OrderBy(i => i).First();
_highest = enumVals.OrderByDescending(i => i).First();
}
public override bool IsValid(int value)
{
return value >= _lowest && value <= _highest;
}
}
Where your loop becomes something like:
你的循环变成这样的:
//Pre import-loop
EnumValidator< MyEnum > enumValidator = EnumValidator< MyEnum >.Create();
while(import) //Tight RT loop.
{
bool isValid = enumValidator.IsValid(theValue);
}
I'm sure the EnumValidator classes could written more efficiently (it's just a quick hack to demonstrate) but quite frankly who cares what happens outside the import loop? The only bit that needs to be super-fast is within the loop. This was the reason for taking the abstract class route, to avoid an unnecessary if-enumContiguous-then-else in the loop (the factory Create essentially does this upfront). You will note a bit of hypocrisy, for brevity this code constrains functionality to int-enums. I should be making use of IConvertible rather than using int's directly but this answer is already wordy enough!
我确信 EnumValidator 类可以更有效地编写(这只是一个快速的演示),但坦率地说,谁在乎导入循环之外会发生什么?唯一需要超快的部分是在循环内。这就是采用抽象类路由的原因,以避免循环中不必要的 if-enumContiguous-then-else(工厂 Create 本质上是预先完成的)。您会注意到一些虚伪,为简洁起见,此代码将功能限制为 int 枚举。我应该使用 IConvertible 而不是直接使用 int 的,但是这个答案已经足够冗长了!
回答by Doug S
As others have mentioned, Enum.IsDefined
is slow, something you have to be aware of if it's in a loop.
正如其他人所提到的,Enum.IsDefined
它很慢,如果它处于循环中,您必须意识到这一点。
When doing multiple comparisons, a speedier method is to first put the values into a HashSet
. Then simply use Contains
to check whether the value is valid, like so:
在进行多重比较时,一种更快的方法是首先将值放入HashSet
. 然后简单地使用Contains
来检查该值是否有效,如下所示:
int userInput = 4;
// below, Enum.GetValues converts enum to array. We then convert the array to hashset.
HashSet<int> validVals = new HashSet<int>((int[])Enum.GetValues(typeof(MyEnum)));
// the following could be in a loop, or do multiple comparisons, etc.
if (validVals.Contains(userInput))
{
// is valid
}
回答by Juan Carlos Velez
To validate if a value is a valid value in an enumeration, you only need to call the static method Enum.IsDefined.
要验证某个值是否为枚举中的有效值,您只需调用静态方法Enum.IsDefined。
int value = 99;//Your int value
if (Enum.IsDefined(typeof(your_enum_type), value))
{
//Todo when value is valid
}else{
//Todo when value is not valid
}
回答by Timo
Here is a fast generic solution, using a statically-constucted HashSet<T>
.
这是一个快速的通用解决方案,使用静态构造的HashSet<T>
.
You can define this once in your toolbox, and then use it for all your enum validation.
您可以在工具箱中定义一次,然后将其用于所有枚举验证。
public static class EnumHelpers
{
/// <summary>
/// Returns whether the given enum value is a defined value for its type.
/// Throws if the type parameter is not an enum type.
/// </summary>
public static bool IsDefined<T>(T enumValue)
{
if (typeof(T).BaseType != typeof(System.Enum)) throw new ArgumentException($"{nameof(T)} must be an enum type.");
return EnumValueCache<T>.DefinedValues.Contains(enumValue);
}
/// <summary>
/// Statically caches each defined value for each enum type for which this class is accessed.
/// Uses the fact that static things exist separately for each distinct type parameter.
/// </summary>
internal static class EnumValueCache<T>
{
public static HashSet<T> DefinedValues { get; }
static EnumValueCache()
{
if (typeof(T).BaseType != typeof(System.Enum)) throw new Exception($"{nameof(T)} must be an enum type.");
DefinedValues = new HashSet<T>((T[])System.Enum.GetValues(typeof(T)));
}
}
}
Note that this approach is easily extended to enum parsing as well, by using a dictionary with string keys (minding case-insensitivity and numeric string representations).
请注意,通过使用带有字符串键的字典(注意不区分大小写和数字字符串表示),这种方法也很容易扩展到枚举解析。
回答by Matt Jenkins
Building upon Timo's answer, I have crafted the following extension method (C# 6 syntax) to provide a fast, generic solution.
基于 Timo 的回答,我精心设计了以下扩展方法(C# 6 语法)以提供快速、通用的解决方案。
This avoids the performance problems of Enum.IsDefined
, and with a much cleaner syntax as a bonus.
这避免了 的性能问题Enum.IsDefined
,并且具有更简洁的语法作为奖励。
public static class EnumHelpers
{
/// <summary>
/// Returns whether the given enum value is a defined value for its type.
/// </summary>
public static bool IsDefined<T>(this T enumValue)
where T : Enum
=> EnumValueCache<T>.DefinedValues.Contains(enumValue);
/// <summary>
/// Caches the defined values for each enum type for which this class is accessed.
/// </summary>
private static class EnumValueCache<T>
where T : Enum
{
public static readonly HashSet<T> DefinedValues = new HashSet<T>((T[])Enum.GetValues(typeof(T)));
}
}
Usage:
用法:
if (!myEnumValue.IsDefined())
// ...