Проверка значений перечисления

Мне нужно проверить целое число, чтобы узнать, является 9X_visual-c# ли оно допустимым значением перечисления.

Как 9X_validation лучше всего это сделать на C#?

89
1

  • Для подхода с флагами может быть полезно проверить этот ответ на повторяющийся вопрос: http://stackoverflow.com/a/23177585/51908 ...
10
Общее количество ответов: 10

Ответ #1

Ответ на вопрос: Проверка значений перечисления

Вы должны любить этих людей, которые считают, что 9X_visual-c# данные всегда поступают не только из пользовательского 9X_visual-c# интерфейса, но и из пользовательского интерфейса, который 9X_c# находится под вашим контролем!

IsDefined подходит 9X_input-validation для большинства сценариев, вы можете начать 9X_input-validation с:

public static bool TryParseEnum(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; } 

(Очевидно, просто отбросьте "this", если 9X_c-sharp вы не считаете, что это подходящее расширение 9X_c#-language типа int)

98
4

  • @jeromej Я спрашивал, почему мы не можем использовать `retVal = (TEnum) enumvalue` вместо` retVal ...

Ответ #2

Ответ на вопрос: Проверка значений перечисления

ИМХО, сообщение, помеченное как ответ, неверно.
Проверка 9X_validate параметров и данных - одна из тех вещей, которые 9X_visual-c# мне вверенили несколько десятилетий назад.

ПОЧЕМУ

Проверка 9X_validation необходима, поскольку перечислению может 9X_validator быть присвоено практически любое целочисленное 9X_visual-c# значение, не вызывая ошибки.
Я потратил 9X_c-sharp много дней на изучение проверки перечисления 9X_c# C#, потому что во многих случаях это необходимая 9X_form-validation функция.

ГДЕ

Основная цель проверки enum для 9X_input-validation меня - проверка данных, прочитанных из файла: вы 9X_validate никогда не узнаете, был ли файл поврежден, изменен 9X_input-validation извне или был специально взломан.
И с проверкой 9X_validator enum данных приложения, вставленных из буфера 9X_c#-language обмена: вы никогда не узнаете, редактировал 9X_validate ли пользователь содержимое буфера обмена.

Тем 9X_enumerations не менее, я провел дни, исследуя и тестируя 9X_visual-c# множество методов, включая профилирование 9X_enum производительности каждого метода, который 9X_c#-language я мог найти или разработать.

Выполнение вызовов 9X_validator чего-либо в System.Enum происходит настолько 9X_c#-language медленно, что это заметно снижает производительность 9X_validator функций, содержащих сотни или тысячи объектов, которые 9X_validation имеют одно или несколько перечислений в 9X_enum своих свойствах, которые должны быть проверены 9X_enum на наличие границ.

Итог: держитесь подальше 9X_c#.net от всего в классе System.Enum при проверке значений 9X_c#-language перечисления, это ужасно медленно.

РЕЗУЛЬТАТ

Метод, который 9X_form-validation я в настоящее время использую для проверки 9X_c-sharp перечисления, вероятно, вызовет у многих 9X_c#.net программистов закатные глаза, но, по-моему, это 9X_form-validation наименьшее зло для моего конкретного дизайна 9X_.cs-file приложения.

Я определяю одну или две константы, которые 9X_enumerations являются верхней и (необязательно) нижней 9X_c#-language границей перечисления, и использую их в 9X_validations паре операторов if() для проверки.
Одним 9X_validations из недостатков является то, что вы должны 9X_enums обязательно обновить константы при изменении 9X_validate перечисления.
Этот метод также работает 9X_validations только в том случае, если стиль перечисления 9X_validation является «автоматическим», где каждый элемент 9X_c#.net перечисления представляет собой инкрементное 9X_c#.net целочисленное значение, такое как 0,1,2,3,4, .... Он 9X_enum не будет работать должным образом с флагами 9X_enums или перечислениями, которые имеют значения, которые 9X_input-validation не являются инкрементными.

Также обратите 9X_validations внимание, что этот метод почти так же быстр, как 9X_c-sharp и обычный, если "<" ">" на обычных int32 9X_validator (которые набрали 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; } 

ЭФФЕКТИВНОСТЬ

Для 9X_c-sharp тех, кому интересно, я описал следующие 9X_enum варианты проверки enum, и вот результаты.

Профилирование 9X_validation выполнялось при компиляции выпуска в цикле 9X_enums из миллиона раз для каждого метода со случайным 9X_c-sharp целочисленным входным значением. Каждый 9X_enumerations тест проводился более 10 раз и усреднялся. Результаты 9X_visual-c# тиков включают общее время выполнения, которое 9X_form-validation будет включать генерацию случайных чисел 9X_.cs-file и т. Д., Но они будут постоянными во всех 9X_enums тестах. 1 тик = 10 нс.

Обратите внимание, что 9X_c# приведенный здесь код не является полным 9X_input-validation тестовым кодом, это только базовый метод 9X_validator проверки перечисления. Было также много 9X_validator дополнительных вариантов, которые были протестированы, и 9X_visual-c# все они дали результаты, аналогичные тем, которые 9X_enumerations показаны здесь, когда тестировалось 1 800 9X_enums 000 тиков.

От самого медленного к самому 9X_enumerations быстрому с округленными результатами, надеюсь, без 9X_form-validation опечаток.

Границы, определенные в методе = 13 600 000 тиков

public static T Clamp(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 9X_validate тиков
Примечание: эта версия кода не ограничивает 9X_form-validation минимальное / максимальное значение, но 9X_validations возвращает значение по умолчанию, если оно 9X_c#.net выходит за границы.

public static T ValidateItem(T eEnumItem) { if (Enum.IsDefined(typeof(T), eEnumItem) == true) return eEnumItem; else return default(T); } 

System.Enum Convert 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; } 

if() Мин. / Макс. Константы = 43 9X_.cs-file 000 тиков = победитель в 42 раза и в 316 9X_validate раз быстрее.

public static MyEnum Clamp(MyEnum value) { if (value < MYENUM_MINIMUM) { return MYENUM_MINIMUM; } if (value > MYENUM_MAXIMUM) { return MYENUM_MAXIMUM; } return value; } 

-eol-

26
2

  • Большое количество наших перечислений не являются смежными, поэтому во многих сценариях это не вариант. 'Enum.IsDefined (typeof (T)' будет медленным в вашем тестовом сценарии, поскольку . ...

Ответ #3

Ответ на вопрос: Проверка значений перечисления

Как уже упоминалось, Enum.IsDefined работает медленно, о 9X_.cs-file чем вы должны знать, если он находится в 9X_validate цикле.

При выполнении множественных сравнений 9X_enums более быстрый метод - сначала поместить 9X_validator значения в HashSet. Затем просто используйте Contains, чтобы 9X_validation проверить, действительно ли значение, например:

int userInput = 4; // below, Enum.GetValues converts enum to array. We then convert the array to hashset. HashSet validVals = new HashSet((int[])Enum.GetValues(typeof(MyEnum))); // the following could be in a loop, or do multiple comparisons, etc. if (validVals.Contains(userInput)) { // is valid } 

15
0

Ответ #4

Ответ на вопрос: Проверка значений перечисления

Вот быстрое универсальное решение, использующее 9X_validation статически построенный HashSet.

Вы можете определить 9X_enums это один раз в своем наборе инструментов, а 9X_form-validation затем использовать его для всей проверки 9X_.cs-file перечисления.

public static class EnumHelpers { /// 
/// Returns whether the given enum value is a defined value for its type. /// Throws if the type parameter is not an enum type. ///
 public static bool IsDefined(T enumValue) { if (typeof(T).BaseType != typeof(System.Enum)) throw new ArgumentException($"{nameof(T)} must be an enum type."); return EnumValueCache.DefinedValues.Contains(enumValue); } /// 
/// 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. ///
 internal static class EnumValueCache { public static HashSet 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[])System.Enum.GetValues(typeof(T))); } } } 

Обратите внимание, что этот 9X_validate подход легко распространяется и на анализ 9X_c-sharp перечисления, используя словарь со строковыми 9X_c-sharp ключами (учитывая нечувствительность к регистру 9X_c-sharp и числовые строковые представления).

14
0

Ответ #5

Ответ на вопрос: Проверка значений перечисления

Брэд Абрамс специально предостерегает от 9X_enum Enum.IsDefined в своем посте The Danger of Oversimplification.

Лучший способ избавиться 9X_validation от этого требования (то есть от необходимости 9X_visual-c# проверять перечисления) — это удалить способы, в 9X_validate которых пользователи могут ошибиться, например, какое-либо 9X_validate поле ввода. Используйте перечисления с раскрывающимися 9X_validate списками, например, чтобы использовать только 9X_c-sharp действительные перечисления.

9
3

  • Рекомендация помещать только перечисления в раскрывающиеся списки отлично работает для WinForms, но не работает для WebForms, где вам нужно проверить пр ...

Ответ #6

Ответ на вопрос: Проверка значений перечисления

Этот ответ является ответом на ответ deegee, который 9X_visual-c# вызывает проблемы с производительностью 9X_enums System.Enum, поэтому его не следует воспринимать 9X_form-validation как мой предпочтительный общий ответ, более 9X_enum адресный для проверки enum в сценариях с 9X_c#.net жесткой производительностью.

Если у вас есть 9X_c#-language критически важная проблема производительности, когда 9X_csharp медленный, но функциональный код запускается 9X_form-validation в жестком цикле, я бы лично посмотрел на 9X_validator то, чтобы вывести этот код из цикла, если 9X_.cs-file это возможно, вместо того, чтобы решать 9X_c# за счет сокращения функциональности. Ограничение 9X_form-validation кода поддержкой только непрерывных перечислений 9X_visual-c# может стать кошмаром для поиска ошибки, если, например, кто-то 9X_enumerations в будущем решит отказаться от некоторых 9X_input-validation значений перечисления. Упрощенно вы можете 9X_c# просто вызвать Enum.GetValues ​​один раз, в 9X_validate самом начале, чтобы избежать срабатывания 9X_validate всех отражений и т. Д. Тысячи раз. Это должно 9X_enums дать вам немедленный рост производительности. Если 9X_.cs-file вам нужна более высокая производительность 9X_form-validation и вы знаете, что многие из ваших перечислений 9X_c# являются смежными (но вы все равно хотите 9X_validation поддерживать перечисления с gappy), вы можете 9X_validate пойти еще дальше и сделать что-то вроде:

public abstract class EnumValidator where TEnum : struct, IConvertible { protected static bool IsContiguous { get { int[] enumVals = Enum.GetValues(typeof(TEnum)).Cast().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 Create() { if (!typeof(TEnum).IsEnum) { throw new ArgumentException("Please use an enum!"); } return IsContiguous ? (EnumValidator)new ContiguousEnumValidator() : new JumbledEnumValidator(); } public abstract bool IsValid(int value); } public class JumbledEnumValidator : EnumValidator where TEnum : struct, IConvertible { private readonly int[] _values; public JumbledEnumValidator() { _values = Enum.GetValues(typeof (TEnum)).Cast().ToArray(); } public override bool IsValid(int value) { return _values.Contains(value); } } public class ContiguousEnumValidator : EnumValidator where TEnum : struct, IConvertible { private readonly int _highest; private readonly int _lowest; public ContiguousEnumValidator() { List enumVals = Enum.GetValues(typeof (TEnum)).Cast().ToList(); _lowest = enumVals.OrderBy(i => i).First(); _highest = enumVals.OrderByDescending(i => i).First(); } public override bool IsValid(int value) { return value >= _lowest && value <= _highest; } } 

Где 9X_validations ваш цикл выглядит примерно так:

//Pre import-loop EnumValidator< MyEnum > enumValidator = EnumValidator< MyEnum >.Create(); while(import) //Tight RT loop. { bool isValid = enumValidator.IsValid(theValue); } 

Я уверен, что 9X_c# классы EnumValidator можно было бы написать 9X_input-validation более эффективно (это всего лишь небольшая 9X_c-sharp демонстрация), но, честно говоря, кого волнует, что 9X_validator происходит вне цикла импорта? Единственный 9X_c-sharp бит, который должен быть сверхбыстрым, - это 9X_c#-language внутри цикла. Это было причиной выбора маршрута 9X_validate абстрактного класса, чтобы избежать ненужного 9X_csharp if-enumContiguous-then-else в цикле (фабрика 9X_enums Create по существу делает это заранее). Вы 9X_enums заметите немного лицемерия, для краткости 9X_.cs-file этот код ограничивает функциональность int-enum. Я 9X_enums должен использовать IConvertible, а не использовать 9X_c-sharp int напрямую, но этот ответ уже достаточно 9X_form-validation многословен!

7
0

Ответ #7

Ответ на вопрос: Проверка значений перечисления

Основываясь на ответе Тимо, я создал следующий 9X_validate метод расширения (синтаксис C# 6), чтобы 9X_enumerations предоставить быстрое универсальное решение.

Это 9X_validator позволяет избежать проблем с производительностью, связанных 9X_enum с Enum.IsDefined, а в качестве бонуса предоставляет более 9X_c#-language чистый синтаксис.

public static class EnumHelpers { /// 
/// Returns whether the given enum value is a defined value for its type. ///
 public static bool IsDefined(this T enumValue) where T : Enum => EnumValueCache.DefinedValues.Contains(enumValue); /// 
/// Caches the defined values for each enum type for which this class is accessed. ///
 private static class EnumValueCache where T : Enum { public static readonly HashSet DefinedValues = new HashSet((T[])Enum.GetValues(typeof(T))); } } 

Использование:

if (!myEnumValue.IsDefined()) // ... 

4
0

Ответ #8

Ответ на вопрос: Проверка значений перечисления

Вот как я это делаю, основываясь на нескольких 9X_c# сообщениях в Интернете. Причина в том, чтобы 9X_validation убедиться, что перечисления, отмеченные 9X_csharp атрибутом Flags, также могут быть успешно проверены.

public static TEnum ParseEnum(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); } } 

1
0

Ответ #9

Ответ на вопрос: Проверка значений перечисления

Вы можете использовать FluentValidation 9X_enums для своего проекта. Вот простой пример для 9X_form-validation «Enum Validation»

Давайте создадим класс 9X_c-sharp EnumValidator с помощью FluentValidation;

public class EnumValidator : AbstractValidator where TEnum : struct, IConvertible, IComparable, IFormattable { public EnumValidator(string message) { RuleFor(a => a).Must(a => typeof(TEnum).IsEnum).IsInEnum().WithMessage(message); } } 

Теперь 9X_form-validation мы создали наш класс enumvalidator; давайте 9X_enums создадим класс для вызова класса enumvalidor;

 public class Customer { public string Name { get; set; } public Address address{ get; set; } public AddressType type {get; set;} } public class Address { public string Line1 { get; set; } public string Line2 { get; set; } public string Town { get; set; } public string County { get; set; } public string Postcode { get; set; } 

}

public enum AddressType { HOME, WORK } 

Пришло 9X_validations время вызвать наш валидатор перечисления 9X_validation для типа адреса в классе клиента.

public class CustomerValidator : AbstractValidator { public CustomerValidator() { RuleFor(x => x.type).SetValidator(new EnumValidator("errormessage"); } } 

1
0

Ответ #10

Ответ на вопрос: Проверка значений перечисления

Объяснить масштабирование производительности 9X_enums конкретно по методу Тимо/Мэтта Дженкинса: Рассмотрим 9X_c# следующий код:

//System.Diagnostics - Stopwatch //System - ConsoleColor //System.Linq - Enumerable Stopwatch myTimer = Stopwatch.StartNew(); int myCyclesMin = 0; int myCyclesCount = 10000000; long myExt_IsDefinedTicks; long myEnum_IsDefinedTicks; foreach (int lCycles in Enumerable.Range(myCyclesMin, myCyclesMax)) { Console.WriteLine(string.Format("Cycles: {0}", lCycles)); myTimer.Restart(); foreach (int _ in Enumerable.Range(0, lCycles)) { ConsoleColor.Green.IsDefined(); } myExt_IsDefinedTicks = myTimer.ElapsedTicks; myTimer.Restart(); foreach (int _ in Enumerable.Range(0, lCycles)) { Enum.IsDefined(typeof(ConsoleColor), ConsoleColor.Green); } myEnum_IsDefinedTicks = myTimer.E Console.WriteLine(string.Format("object.IsDefined() Extension Elapsed: {0}", myExt_IsDefinedTicks.ToString())); Console.WriteLine(string.Format("Enum.IsDefined(Type, object): {0}", myEnum_IsDefinedTicks.ToString())); if (myExt_IsDefinedTicks == myEnum_IsDefinedTicks) { Console.WriteLine("Same"); } else if (myExt_IsDefinedTicks < myEnum_IsDefinedTicks) { Console.WriteLine("Extension"); } else if (myExt_IsDefinedTicks > myEnum_IsDefinedTicks) { Console.WriteLine("Enum"); } } 

Вывод начинается следующим 9X_enums образом:

Cycles: 0 object.IsDefined() Extension Elapsed: 399 Enum.IsDefined(Type, object): 31 Enum Cycles: 1 object.IsDefined() Extension Elapsed: 213654 Enum.IsDefined(Type, object): 1077 Enum Cycles: 2 object.IsDefined() Extension Elapsed: 108 Enum.IsDefined(Type, object): 112 Extension Cycles: 3 object.IsDefined() Extension Elapsed: 9 Enum.IsDefined(Type, object): 30 Extension Cycles: 4 object.IsDefined() Extension Elapsed: 9 Enum.IsDefined(Type, object): 35 Extension 

Похоже, это указывает на высокую 9X_validations стоимость установки статического объекта 9X_c#.net hashset (в моей среде примерно 15-20 мс. Изменение 9X_validations того, какой метод вызывается первым, не 9X_c#-language меняет того факта, что первый вызов метода 9X_c#.net расширения (для настройки статического хэш-набора) довольно 9X_form-validation длинный. Enum.IsDefined(typeof(T), object) также длиннее обычного для первого 9X_validator цикла, но, что интересно, гораздо меньше.

Исходя 9X_enum из этого, кажется, что Enum.IsDefined(typeof(T), object) на самом деле быстрее 9X_validation до lCycles = 50000 или около того.

Я не уверен, почему Enum.IsDefined(typeof(T), object) становится 9X_validator быстрее как при 2, так и при 3 поиске, прежде 9X_c# чем начнет расти. Ясно, что происходит какой-то 9X_validate внутренний процесс, так как object.IsDefined() также занимает 9X_c#-language заметно больше времени для первых 2 поисков, прежде 9X_validation чем он начнет быстро истекать кровью.

Другой 9X_c# способ сформулировать это так: если вам 9X_c-sharp нужно выполнить множество операций поиска 9X_c#.net с любой другой удаленно длительной операцией 9X_enum (например, файловой операцией, такой как 9X_enumerations открытие), которая добавит несколько миллисекунд, первоначальная 9X_validate настройка для object.IsDefined() будет поглощена (особенно 9X_form-validation если асинхронный) и становятся практически 9X_validation незаметными. В этот момент Enum.IsDefined(typeof(T), object) выполняется 9X_form-validation примерно в 5 раз дольше.

В принципе, если 9X_c# у вас нет буквально тысяч вызовов для одного 9X_c-sharp и того же Enum, я не уверен, как хеширование 9X_validator содержимого сэкономит вам время при выполнении 9X_.cs-file вашей программы. Enum.IsDefined(typeof(T), object) может иметь концептуальные 9X_validation проблемы с производительностью, но, в конечном 9X_validations счете, он достаточно быстр, пока он вам 9X_enum не понадобится тысячи раз для одного и того 9X_visual-c# же перечисления.

Интересно отметить, что 9X_enumerations реализация ValueCache в качестве гибридного 9X_enums словаря дает время запуска, которое достигает 9X_enum паритета с Enum.IsDefined(typeof(T), object) за ~1500 итераций. Конечно, использование 9X_validate HashSet проходит через ~ 50 КБ.

Итак, мой 9X_form-validation совет: если вся ваша программа проверяет 9X_validate одно и то же перечисление (проверка разных 9X_form-validation перечислений вызывает одинаковый уровень 9X_.cs-file задержки запуска, один раз для каждого отдельного 9X_c#.net перечисления) менее 1500 раз, используйте 9X_enum Enum.IsDefined(typeof(T), object). Если у вас от 1500 до 50 КБ, используйте 9X_enumerations HybridDictionary для своего хеш-набора, начальное 9X_form-validation заполнение кеша происходит примерно в 10 9X_validate раз быстрее. Что-нибудь более 50 тысяч итераций, HashSet 9X_c-sharp является довольно явным победителем.

Также 9X_validations имейте в виду, что мы говорим в Ticks. В 9X_enumerations .Net 10 000 тиков — это 1 мс.

Для полного 9X_.cs-file раскрытия информации я также протестировал 9X_enums List в качестве кеша, и он занимает около 9X_c# 1/3 времени заполнения хэш-набора, однако 9X_c-sharp для любого перечисления более 9 или около 9X_enum того элементов это намного медленнее, чем 9X_form-validation любой другой метод. Если все ваши перечисления 9X_enum меньше 9 элементов (или еще меньше), это 9X_visual-c# может быть самый быстрый подход.

Кэш, определенный 9X_validate как HybridDictionary (да, ключи и значения 9X_c#-language одинаковы. Да, его немного сложнее читать, чем 9X_enums более простые ответы, упомянутые выше):

//System.Collections.Specialized - HybridDictionary private static class EnumHybridDictionaryValueCache where T : Enum { static T[] enumValues = (T[])Enum.GetValues(typeof(T)); static HybridDictionary PopulateDefinedValues() { HybridDictionary myDictionary = new HybridDictionary(enumValues.Length); foreach (T lEnumValue in enumValues) { //Has to be unique, values are actually based on the int value. Enums with multiple aliases for one value will fail without checking. //Check implicitly by using assignment. myDictionary[lEnumValue] = lEnumValue; } return myDictionary; } public static readonly HybridDictionary DefinedValues = PopulateDefinedValues(); } 

1
0