Здравствуйте, Аноним, Вы писали:
А>Есть у меня код
А>А> [Flags]
А> public enum SystemOptions
А> {
А> None = 0,
А> RecompileApplication = 1,
А> ShowModelEditor = 2
А> }
А> static void Main(string[] args)
А> {
А> SystemOptions s = (SystemOptions) (- 1);
А> Console.WriteLine(s.ToString());
А> Console.ReadLine();
А> }
А> }
А>
А> Почему он выполняется без проблем?
Я думаю, что преобразование вида (SomeEnum)42 не падает во время исполнения из соображений эффективности. У разработчиков перечислений было несколько вариантов:
1) запретить явное преобразование нижележащего целочисленного типа к перечислению (кстати, неявное преобразование запрещено для всех значений кроме 0). Плохо, поскольку это ограничение может ограничить использование перечислений там, где они могли бы использоваться.
2) разрешить явное преобразование и проверять значение во время исполнения. Плохо, поскольку это потребует достаточно серьезных проверок во время исполнения.
3) разрешить явное преобразование и переложить проверку "валидности" значения на разработчика. Может и не идеально, но лучше, чем другие варианты.
Правила использования перечислений подразумевают проверку граничных условий, либо с помощью секции default конструкции switch, либо путем добавления такого трюка:
public enum SomeEnum {
MinValidValue = 1,
Value1 = 1,
Value2 = 2,
MaxValue = Value2,
}
var value = (SomeEnum)(ReadFromSomewhere());
if (value < SomeEnum.MinValidValue || value > SomeEnum.MaxValidValue)
throw new InvalidOperationException("Ooooppps!!!!!111");
A> Я предполагал, что если -1 приведу к Enum, то он включит мне все флаги.
Он и включает все флаги, и следующая проверка это доказывает:
Попробуйте выполнить следующий код:
bool isRecompileApplication = (s & SystemOptions.RecompileApplication) != 0;
bool isShowModelEditor = (s & SystemOptions.ShowModelEditor) != 0;
isRecompileApplication.Dump(); // True
isShowModelEditor.Dump(); // True
Поэтому здесь проблема не с выставлением флагов, а с получением строкового представления флагового перечисления, у которого тоже есть объяснение.
Вот более простой пример:
var s1 = (SystemOptions)3;
Console.WriteLine(s1);
var s2 = (SystemOptions)7;
Console.WriteLIne(s2);
В первом случае мы получим: RecompileApplication, ShowModelEditor.
А во втором случае мы получим: 7 (!!!!)
Я не увидел объяснения поведения в спецификации (видимо, это оставлено на усмотрение разработчиков). Но логика в этом все же есть.
Что вы ожидаете во втором случае (в случае с переменной s2): что на консоли будет выведено "RecompileApplication, ShowModelEditor"? Но ведь это не логично, поскольку эквивалентное строковое представление подразумевает эквивалентность и самого значения (что, в данном случае, явно не так). Что тогда? Выводить: "RecompileAppliction, ShowModelEditor, 7"? Тоже не логично.
Вот и получается, что при выводе флагового перечисления на экран, если у нас взведен хотя бы один бит, которому не соответствует ни один флаг разработчики выводят голое значение перечисления.