2017-01-23 12 views
2

Я хорошо знаю, что C# не разрешает readonly полей в switch блоках, что является то, что this question адресов.В чем причина того, что вы не можете использовать поля readonly в блоках переключателей?

Я хотел бы понять почему это случай. Это просто произвольная спецификация языка, или есть техническая причина этого, и если да, то какова техническая причина?

Позвольте мне пояснить, что я понимаю разницу между const и readonly, и я знаю, что C# switchconst требует значения или значения известны во время компиляции. Для меня, функционально, используя кучу if..else if утверждений имеет тот же результат, что и использование switch заявления, потому что все, что я могу сделать с switch заявлением я могу достичь с if, а также, например:

const int MyConstantValue = 10; 

int myCompareValue = 3; 
if(myCompareValue == MyConstantValue) 
{ 
    //... 
} 
else 
{ 
    //... 
} 

switch(myCompareValue) 
{ 
    case MyConstantValue: 
    //... 
    break; 
    default: 
    //... 
    break; 
} 

Обе эти конструкции имеют одинаковый результат: выполняется блок else или default, но if может выполнять его без констант времени компиляции или известных значений. Почему if делать то, что switch не может?

+0

Переключатели 'C#' моделируются после' C/C++ 'переключателей, которые имеют одинаковое ограничение. Причина в производительности: оператор switch может быть скомпилирован в очень эффективную «таблицу перехода», что было бы невозможно, если случаи не были известны во время компиляции. Таким образом, ответ заключается в том, чтобы «обеспечить, чтобы оператор switch был выполнен». Другим моментом является то, что оператор switch имеет явно уникальные случаи во время компиляции, но без постоянных случаев, которые не были бы доказуемы во время компиляции. –

+0

@MatthewWatson, это то, что я искал, опубликовать ваш комментарий в качестве ответа, и я его приму. – Logan

+0

Ваше недоразумение начинается с * То, что я не понимаю, это функционально, используя кучу if..else, если утверждения имеют одинаковый результат *. Это не! Прочитайте комментарий Matthews. –

ответ

3

Причина в том, что коммутаторы C# моделируются после коммутаторов C/C++, которые имеют одинаковое ограничение.

Есть две причины для этого ограничения:

  • Производительность: Оператор переключатель может быть собран в очень эффективный «прыжок стола», который не был бы возможен, если случаи не были известны время компиляции.
  • Корректность: Оператор switch имеет явно уникальные случаи во время компиляции, но без постоянных случаев, которые не были бы доказуемы во время компиляции.
2

В качестве this состояния документации, поля readonly являются константой времени выполнения. (В отличие от константных полей).

Между тем, операторы switch ожидают инициализированное значение во время компиляции.

Поэтому компилятор отклоняет поля readonly.

Редактировать: Не специалист по компилятору C#, но кажется, что он создает таблицу ветвей в IL, а не if-statements (в некоторых случаях). Для получения дополнительной информации посетите this blog post.

Цитирует соответствующую часть:

... первый раз, когда функция называется словарем строк (ключ) и INT (значение) создаются и все случаи хранящихся в этом словаре поскольку ключ и целое число в качестве значения - , хранящиеся на нем. Затем для оператора switch строка принимается и запрашивается в словаре, и если она присутствует, возвращается числовое значение для строки. Используя это число, компилятор создает эффективную таблицу прыжка , и она переходит к целевой строке Console.Writeline .

Теперь ответ:. Строки предварительно хранятся в словаре. Если строки в заявлении дела не были константами, изменения в них не отражаются в словаре, и, следовательно, вы подойдете к сравнению против устаревшего значения. Чтобы избежать этой несогласованности, непостоянные значения вообще не поддерживаются.

Очевидно, что для динамических значений словарь не может использоваться, и, следовательно, нет возможности оптимизации для коммутационного футляра, поэтому в любом случае использовать if-then-else.

+0

Это не отвечает * почему * оператор switch требует констант времени компиляции. Этот ответ - именно то, что обсуждается в вопросе, который я задал в моем вопросе. – Logan

+0

Чтобы уточнить: переменные 'readonly' задаются из конструктора. Переменные 'const' задаются во время компиляции. –

+0

@Logan это не ваш вопрос. –

-1

Его так как переменные readonly не являются постоянными. Они могут быть назначены в объявлении или в конструкторе класса. Поэтому его значение неизвестно во время разработки.

+0

Это не отвечает на мой вопрос * почему * это так. – Logan

1

Подумайте о том, что компилятор делает с switch блока: она по существу строит серию условных переходов, на основе целевых значений соответствующего выражения, которые известны во время компиляции.

Теперь предположим, что у вас это:

private readonly int N = System.DateTime.Ticks; 

Если вы пытаетесь использовать N в качестве целевого значения в switch блока, то значение не известно во время компиляции. Итак, что должен делать компилятор? Хотя он мог бы, конечно, построить более сложную серию условных ветвей, действительно ли это было бы оправдано «полезностью» этого шаблона?

Личен, есть и другие вещи, которые я предпочел бы сценаристы компилятора потратили свое время на ...

1

переключателя этикеток можно компилировать только постоянное время (по крайней мере до тех пор, C# 7 судов, где switch также будет полезно для сопоставления с образцом). Поле readonly не является константой времени компиляции, ее поле инициализируется при выполнении кода.

Единственная разница между полем readonly и регулярным полем состоит в том, что первая может быть инициализирована только в конструкторе или через инициализатор поля, а последняя может быть инициализирована/переназначена где угодно.

Чтобы увидеть различию между только для чтения поля или константы, просто рассмотрим следующий код:

bool const True = true; 

if (!true) 
    throw new Exception(); //Compiler warning: unreachable code detected 

return; 

Здесь, компилятор знает, что True во время компиляции и может поэтому рассуждать о достижимости заявления о броске, которое, очевидно, недостижимо; if (false) всегда будет игнорировать следующий блок/оператор.

Теперь рассмотрим следующее:

readonly static bool True = true; 

if (!true) 
    throw new Exception(); 

return; 

Это будет компилировать просто отлично. Компилятор не может знать, что True фактически является true до тех пор, пока не будет инициализирован класс, содержащий поле. Это означает, что код должен запустить, и только время выполнения может привести к тому, что True действительно есть.

Что касается причин, почему switch кейсы должны быть постоянными, я считаю, что в основном это связано с соображениями оптимизации (более эффективный код) и анализом покрытия; компилятор может рассуждать о том, обрабатываются все возможные случаи или нет, что очень полезно во многих сценариях и невозможно с не постоянными метками.

Если вам нужно разветвиться на основе не постоянных выражений, просто используйте ifelse ifelse.