8 ответов:
вы должны любить этих людей, которые предполагают, что данные не только всегда поступают из пользовательского интерфейса, но и пользовательский интерфейс под вашим контролем!
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)
IMHO сообщение, отмеченное как ответ, неверно.
Проверка параметров и данных - это одна из вещей, которые были просверлены во мне десятилетия назад.почему
проверка требуется, потому что по существу любое целочисленное значение может быть присвоено перечислению без возникновения ошибки.
Я провел много дней, исследуя проверку перечисления C#, потому что во многих случаях это необходимая функция.здесь
главная цель проверки enum для меня заключается в проверке данных, прочитанных из файла: вы никогда не знаете, был ли файл поврежден, или был изменен внешне, или был взломан специально.
И с перечислением проверки данных приложения, вставленных из буфера обмена: вы никогда не знаете, если пользователь редактировал содержимое буфера обмена.тем не менее, я потратил дни на исследование и тестирование многих методов, включая профилирование производительности каждого метода, который я мог найти или спроектировать.
оформление звонит во что-нибудь в системе.Перечисление настолько медленно, что это было заметное снижение производительности для функций, которые содержали сотни или тысячи объектов, которые имели одно или несколько перечислений в своих свойствах, которые должны были быть проверены для границ.
итог, держитесь подальше от все в системе.Класс Enum при проверке значений enum он ужасно медленный.
результат
метод, который я в настоящее время использую для подтверждения перечисления вероятно, это привлечет внимание многих программистов здесь, но это имхо наименьшее зло для моего конкретного дизайна приложения.
Я определяю одну или две константы, которые являются верхней и (необязательно) нижней границами перечисления, и использую их в паре операторов if() для проверки.
Одним из недостатков является то, что вы должны быть уверены, чтобы обновить константы, если вы измените перечисление.
Этот метод также работает только в том случае, если перечисление является "автоматическим" стилем, где каждый элемент перечисления является инкрементным целочисленное значение, такое как 0,1,2,3,4,.... Он не будет работать должным образом с флагами или перечислениями, которые имеют значения, которые не являются инкрементными.также обратите внимание, что этот метод почти так же быстро, как обычный, если "" на регулярных int32s (который набрал 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; }производительность
для тех, кто заинтересован, я профилировал следующие варианты проверки перечисления, и вот результаты.
профилирование было выполнено при компиляции выпуска в цикле по одному миллиону раз для каждого метода со случайным целочисленным входным значением. Каждый тест выполнялся более 10 раз и усреднялся. Результаты тика включают общее время выполнения, которое будет включать генерацию случайных чисел и т. д. но они будут постоянными во всех тестах. 1 тик = 10нс.
обратите внимание, что код здесь не является полным тестовым кодом, это только основной метод проверки перечисления. Там были также много дополнительных вариаций на них, которые были протестированы, и все они с результатами, похожими на показанные здесь, что benched 1,800,000 тиков.
перечислены самые медленные и быстрые с округленными результатами, надеюсь, нет опечаток.
границы, определенные в методе = 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; }перечисление.Т. е. = 1,800,000 клещей
Примечание: эта версия кода не зажимается до Min / Max, но возвращает значение по умолчанию, если из границы.public static T ValidateItem<T>(T eEnumItem) { if (Enum.IsDefined(typeof(T), eEnumItem) == true) return eEnumItem; else return default(T); }
Брэд Абрамс специально предупреждает против
Enum.IsDefinedв должности опасность чрезмерного упрощения.лучший способ избавиться от этого требования (то есть от необходимости проверять перечисления) - это удалить способы, с помощью которых пользователи могут ошибаться, например, какое-то поле ввода. Используйте перечисления с выпадающими списками, например, для применения только допустимых перечислений.
как уже упоминалось,
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 }
этот ответ является ответом на ответ Диджи, который поднимает вопросы производительности системы.Перечисление so не следует воспринимать как мой предпочтительный общий ответ, больше обращаясь к проверке перечисления в жестких сценариях производительности.
Если у вас есть критически важная проблема с производительностью, когда медленный, но функциональный код запускается в узком цикле, то я лично буду смотреть на перемещение этого кода из цикла, если это возможно, вместо решения путем уменьшения функциональности. Ограничение кода чтобы поддерживать только непрерывные перечисления, может быть кошмаром найти ошибку, если, например, кто-то в будущем решит отказаться от некоторых значений перечисления. Упрощенно можно просто назвать перечисление.GetValues один раз, прямо в начале, чтобы избежать запуска всех отражений и т. д. тысячи раз. Это должно дать вам немедленное повышение производительности. Если вам нужно больше производительности, и вы знаете, что многие из ваших перечислений являются смежными (но вы все еще хотите поддерживать "зияющие" перечисления), вы можете пойти дальше и сделать что-то вроде:
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 в цикле (создание фабрики по сути, делает это заранее). Вы заметите немного лицемерия, для краткости этот код ограничивает функциональность int-enums. Я должен использовать IConvertible, а не использовать int напрямую, но этот ответ уже достаточно многословен!
вот как я это делаю на основе нескольких сообщений в интернете. Причина этого заключается в том, чтобы убедиться, что перечисления отмечены
Я нашел это ссылке это отвечает на него довольно хорошо. Он использует:
(ENUMTYPE)Enum.ToObject(typeof(ENUMTYPE), INT)
чтобы проверить, является ли значение допустимым значением в перечислении, вам нужно только вызвать статический метод перечисление.Т. е..
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 }
Comments