Re: Почему можно -1 привести к Enum
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 21.03.13 09:04
Оценка: 10 (2)
Здравствуйте, Аноним, Вы писали:

А>Есть у меня код


А>
А> [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"? Тоже не логично.

Вот и получается, что при выводе флагового перечисления на экран, если у нас взведен хотя бы один бит, которому не соответствует ни один флаг разработчики выводят голое значение перечисления.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.