80LevelElf about IT Записи Мои проекты Обо мне
По работе наткнулся на очень интересный баг (дело происходит в .Net). Есть у нас вот такой вот enum: public enum MyEnum { Value1, Value2, Value3, Value4, … //A lot of values } И где-то он парсится из обычной строки. Только вот строка пришла к нам из файла и оказалась немного корявая: "Value1, Value2" Ну корявая и корявая, значит не обработается – а что они хотели, присылая нам такие значения? Парсим: MyEnum result; if (Enum.TryParse("Value1, Value2", true, out result)) Console.WriteLine(result.ToString()); else Console.WriteLine("A wrong value!"); Иии как вы поняли оно распарсилось (иначе бы я не написал этот пост). А теперь угадайте, какое значение получилось в итоге? Value4! Но почему? [Разобраться нам помогут исходники .Net’a](http://referencesource.microsoft.com/#mscorlib/system/enum.cs,bdb5b3bd2fb82fff,references ): Нас интересует метод: private static bool TryParseEnum(Type enumType, String value, bool ignoreCase, ref EnumResult parseResult) Где-то в середине метода у нас имеется нужный нам код: String[] values = value.Split(enumSeperatorCharArray); // Find the field.Lets assume that these are always static classes because the class is // an enum. ValuesAndNames entry = GetCachedValuesAndNames(rtType, true); String[] enumNames = entry.Names; ulong[] enumValues = entry.Values; for (int i = 0; i < values.Length; i++) { values[i] = values[i].Trim(); // We need to remove whitespace characters bool success = false; for (int j = 0; j < enumNames.Length; j++) { if (ignoreCase) { if (String.Compare(enumNames[j], values[i], StringComparison.OrdinalIgnoreCase) != 0) continue; } else { if (!enumNames[j].Equals(values[i])) continue; } ulong item = enumValues[j]; result |= item; success = true; break; } if (!success) { // Not found, throw an argument exception. parseResult.SetFailure(ParseFailureKind.ArgumentWithParameter, "Arg_EnumValueNotFound", value); return false; } } // И так далее Как мы можем видеть логика здесь такая: 1. Разбиваем переданную строку по символу запятой (лидирующие и последующие пробелы каждой части пропускаются). 2. Пробуем парсить каждую часть. 3. Все удачно распарсенные части складываются вместе с помощью бинарного ИЛИ. Особо хотелось бы заметить на одно (на мой взгляд) не очень удачное решение: если даже какая-то из частей комплексного enum’a не будет распарсена, мы все равно получим валидное значение (включая просто число): public enum MyEnum { Value1 = 0, Value2 = 1, Value3 = 2 } MyEnum result; if (Enum.TryParse("Value1, Value2, Value3", true, out result)) Console.WriteLine(result.ToString()); else Console.WriteLine("A wrong value!"); В данном случае будет выведено вообще 3 (так как 00 | 01 | 10 = 11), но у нас нет такого значения, что не мешает такому enum’у существовать (для проверки на подобные значения [см Enum.IsDefined()](https://msdn.microsoft.com/ru-ru/library/system.enum.isdefined(v=vs.110).aspx)).
(01.08.2017)

blog comments powered by Disqus