Проверка значений перечисления
Мне нужно проверить целое число, чтобы узнать, является 9X_visual-c# ли оно допустимым значением перечисления.
Как 9X_validation лучше всего это сделать на C#?
- Для подхода с флагами может быть полезно проверить этот ответ на повторяющийся вопрос: http://stackoverflow.com/a/23177585/51908 ...
Ответ #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)
- @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-
- Большое количество наших перечислений не являются смежными, поэтому во многих сценариях это не вариант. '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
}
Ответ #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 и числовые строковые представления).
Ответ #5
Ответ на вопрос: Проверка значений перечисления
Брэд Абрамс специально предостерегает от 9X_enum Enum.IsDefined
в своем посте The Danger of Oversimplification.
Лучший способ избавиться 9X_validation от этого требования (то есть от необходимости 9X_visual-c# проверять перечисления) — это удалить способы, в 9X_validate которых пользователи могут ошибиться, например, какое-либо 9X_validate поле ввода. Используйте перечисления с раскрывающимися 9X_validate списками, например, чтобы использовать только 9X_c-sharp действительные перечисления.
- Рекомендация помещать только перечисления в раскрывающиеся списки отлично работает для 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
Ответ на вопрос: Проверка значений перечисления
Основываясь на ответе Тимо, я создал следующий 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())
// ...
Ответ #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);
}
}
Ответ #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");
}
}
Ответ #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();
}
-
2
-
5
-
2
-
5
-
4
-
2
-
1
-
12
-
27
-
32
-
11
-
20
-
11
-
31
-
11
-
12
-
14
-
4
-
31
-
6
-
10
-
20
-
11
-
7
-
5
-
11
-
5
-
5
-
6
-
4
-
17
-
5
-
11
-
4
-
10
-
14
-
18
-
4
-
10
-
17
-
7
-
25
-
8
-
9
-
7
-
9
-
9
-
8
-
8
-
10