validation枚举值
我需要validation一个整数来知道如果是一个有效的枚举值。
什么是在C#中做到这一点的最好方法?
你必须爱这些人认为数据不仅总是来自用户界面,而是你的控制权的UI!
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; }
(很明显,如果你不认为这是一个合适的int扩展,就放弃'this'
恕我直言,标记为答案的post是不正确的。
参数和数据validation是几十年前钻入我的东西之一。
为什么
validation是必需的,因为基本上任何整数值都可以分配给枚举而不会引发错误。
我花了很多时间研究C#枚举validation,因为在许多情况下它是一个必要的function。
哪里
枚举validation的主要目的在于validation从文件中读取的数据:您永远不知道文件是否已被破坏,是否被外部修改,或被故意破解。
通过对从剪贴板粘贴的应用程序数据进行枚举validation:您永远不会知道用户是否编辑了剪贴板内容。
也就是说,我花了好几天的时间研究和testing了很多方法,包括分析我能find或devise的每种方法的性能。
在System.Enum中调用任何东西的速度非常缓慢,对于包含数百或数千个对象的函数来说,这是一个明显的性能损失,这些对象的属性中有一个或多个枚举,必须对边界进行validation。
底线,在validation枚举值时远离System.Enum类中的所有内容 ,这是非常慢的。
结果
我目前用于枚举validation的方法可能会让许多程序员在这里画滚动的眼睛,但对于我的具体应用程序devise来说,这是最不起眼的。
我定义了一个或两个常量,它们是枚举的上限和(可选)下限,并在一对if()语句中用于validation。
一个缺点是,如果更改枚举,则必须确保更新常量。
这种方法也只适用于枚举是一个“自动”风格,其中每个枚举元素是一个增量整数值,如0,1,2,3,4,….它不能正常工作的标志或枚举具有不增量的值。
另外请注意,如果在普通的int32s(在我的testing中得38,000个滴答声)上的“<”“>”“,这个方法几乎和常规一样快。
例如:
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; }
性能
对于那些有兴趣的人,我在枚举validation上描述了以下变体,这里是结果。
在一个随机整数input值的每一个方法上,在一百万次的循环中进行释放编译。 每个testing运行超过10次并取平均值。 勾号结果包括总执行时间,其中包括随机数的生成等,但这些将在testing中保持不变。 1滴答= 10ns。
请注意,这里的代码不是完整的testing代码,它只是基本的枚举validation方法。 在这些testing中还有很多额外的变化,所有这些变化的结果与这里所显示的结果相似,即180万个蜱。
列出最慢和最快的四舍五入的结果,希望没有错别字。
方法确定的边界 = 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个刻度
注意:这个代码版本不会限制为最小值/最大值,但如果超出范围则返回默认值。
public static T ValidateItem<T>(T eEnumItem) { if (Enum.IsDefined(typeof(T), eEnumItem) == true) return eEnumItem; else return default(T); }
System.Enum转换Int32转换 = 1,800,000滴答
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; }
如果()最小/最大常量 = 43000滴答=赢家42倍和316x更快。
public static MyEnum Clamp(MyEnum value) { if (value < MYENUM_MINIMUM) { return MYENUM_MINIMUM; } if (value > MYENUM_MAXIMUM) { return MYENUM_MAXIMUM; } return value; }
-eol-
布拉德·艾布拉姆斯特别警告Enum.IsDefined
。他的Enum.IsDefined
在“ Enum.IsDefined
的危险”一文中 。
摆脱这种需求(也就是需要validation枚举)的最好方法是去掉用户可能弄错的方式,例如某种input框。 例如,使用具有下拉列表的枚举来强制执行有效的枚举。
这个答案是回应deegee的回答,这引起了System.Enum的性能问题,因此不应该被看作是我的首选通用答案,更多的是在严格的性能场景中处理枚举validation。
如果你有一个任务关键的性能问题,在一个紧密的循环中运行速度慢但function性的代码,那么我个人会考虑把代码从循环中移出,而不是通过减lessfunction来解决。 如果将代码限制为仅支持连续的枚举,那么如果某个人今后决定贬低一些枚举值,那么发现一个bug可能是一个噩梦。 简单地说,你可以在一开始就调用Enum.GetValues一次,以避免触发所有的reflection等数千次。 这应该会立即提升你的performance。 如果你需要更多的performance,并且你知道你的很多枚举是连续的(但是你仍然想支持'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; } }
你的循环变成这样的东西:
//Pre import-loop EnumValidator< MyEnum > enumValidator = EnumValidator< MyEnum >.Create(); while(import) //Tight RT loop. { bool isValid = enumValidator.IsValid(theValue); }
我相信EnumValidator类可以更高效地写入(这只是一个简单的示例),但是坦率地说,谁在乎在导入循环之外发生了什么呢? 唯一需要超快速的是在循环中。 这是采取抽象类路由的原因,以避免循环中不必要的if-enumContiguous-then-else(工厂创build本质上是这样做的)。 你会注意到一些虚伪,为简洁起见,这段代码将function限制在int-enums中。 我应该使用IConvertible而不是直接使用int,但是这个答案已经够罗嗦了!
正如其他人所说, Enum.IsDefined
是慢的,你必须知道,如果它在一个循环。
在进行多重比较时,更快的方法是首先将值放入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 }
这就是我如何根据多个post在网上做的。 这样做的原因是为了确保标记为Flags
属性的枚举也可以被成功validation。
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); } }
我发现这个链接很好地回答。 它用:
(ENUMTYPE)Enum.ToObject(typeof(ENUMTYPE), INT)
要validation枚举中某个值是否为有效值,只需调用静态方法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 }