Я писал этот код:
private static Expression<Func<Binding, bool>> ToExpression(BindingCriterion criterion)
{
switch (criterion.ChangeAction)
{
case BindingType.Inherited:
var action = (byte)ChangeAction.Inherit;
return (x => x.Action == action);
case BindingType.ExplicitValue:
var action = (byte)ChangeAction.SetValue;
return (x => x.Action == action);
default:
// TODO: Localize errors
throw new InvalidOperationException("Invalid criterion.");
}
}
И был удивлен, обнаружив ошибку компиляции:
В этой области уже определена локальная переменная с именем 'action'
Это было довольно легко решить проблему; просто избавиться от второго var
сделал свое дело.
Очевидно, что переменные, объявленные в case
блоках, имеют родительскую область switch
, но мне любопытно, почему это так. Учитывая , что C # не позволяет выполнение провалиться других случаях (это требует break
, return
, throw
или goto case
заявления в конце каждого case
блока), кажется , довольно странно , что это позволило бы объявления переменных в один , case
чтобы использовать или конфликт с переменными в любой другой case
, Другими словами переменные, кажется, проваливаются через case
операторы, хотя выполнение не может C # делает все возможное, чтобы улучшить читабельность, запретив некоторые конструкции других языков, которые сбивают с толку или легко злоупотребляют. Но похоже, что это просто может вызвать путаницу. Рассмотрим следующие сценарии:
Если бы изменить это на это:
case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action); case BindingType.ExplicitValue: return (x => x.Action == action);
Я получаю « Использование неназначенной локальной переменной« действие » ». Это сбивает с толку, потому что в любой другой конструкции в C #, о которой я могу думать
var action = ...
, инициализируется переменная, но здесь она просто объявляет ее.Если бы я поменялся местами так:
case BindingType.ExplicitValue: action = (byte)ChangeAction.SetValue; return (x => x.Action == action); case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action);
Я получаю « Не могу использовать локальную переменную« действие », прежде чем она объявлена ». Таким образом, порядок использования блоков case здесь важен не совсем очевидным образом. Обычно я могу записывать их в любом порядке, который мне нужен, но поскольку он
var
должен появляться в первом используемом блокеaction
, я должен настроитьcase
блоки. соответственно.Если бы изменить это на это:
case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action); case BindingType.ExplicitValue: action = (byte)ChangeAction.SetValue; goto case BindingType.Inherited;
Тогда я не получаю ошибки, но в некотором смысле похоже, что переменной присваивается значение до ее объявления.
(Хотя я не могу вспомнить, когда бы вы на самом деле захотели это сделать - я даже не знал, чтоgoto case
существовал до сегодняшнего дня)
Итак, мой вопрос: почему разработчики C # не дали case
блокам собственную локальную область видимости? Есть ли для этого исторические или технические причины?
источник
action
переменную передswitch
оператором или поместите каждый случай в свои фигурные скобки, и вы получите разумное поведение.switch
вообще, не используя a - мне просто любопытно обосновать этот дизайн.break
невозможна в C #.Ответы:
Я думаю, что хорошей причиной является то, что в любом другом случае область действия «нормальной» локальной переменной является блоком, ограниченным фигурными скобками (
{}
). Локальные переменные, которые не являются нормальными, появляются в специальной конструкции перед оператором (который обычно является блоком), какfor
переменная цикла или переменная, объявленная вusing
.Еще одним исключением являются локальные переменные в выражениях запросов LINQ, но они полностью отличаются от обычных объявлений локальных переменных, поэтому я не думаю, что есть вероятность путаницы там.
Для справки, правила приведены в §3.7 Области применения спецификации C #:
(Хотя я не совсем уверен, почему этот
switch
блок явно упоминается, поскольку у него нет специального синтаксиса для отклонений локальных переменных, в отличие от всех других упомянутых конструкций.)источник
switches
похоже, веду себя одинаково в этом отношении (за исключением необходимости переходов в концеcase
). Кажется, это поведение было просто скопировано оттуда. Поэтому я думаю, что короткий ответ -case
операторы не создают блоки, они просто определяют подразделенияswitch
блока - и, следовательно, сами по себе не имеют границ.Но это так. Вы можете создавать локальные области где угодно, заключая строки в
{}
источник
Я процитирую Эрика Липперта, ответ которого достаточно ясен по этому вопросу:
Так что, если вы не работаете в команде разработчиков C # 1999 больше, чем Эрик Липперт, вы никогда не узнаете точную причину!
источник
Объяснение простое - это потому, что это похоже на то, что в C. Такие языки, как C ++, Java и C #, скопировали синтаксис и область видимости оператора switch для удобства.
(Как указано в другом ответе, разработчики C # не имеют документации о том, как было принято это решение в отношении кейс-областей. Но неустановленный принцип для синтаксиса C # заключался в том, что, если у них не было веских причин сделать это по-другому, они копировали Java. )
В C операторы case похожи на goto-label. Оператор switch - действительно лучший синтаксис для вычисляемого перехода . Случаи определяют точки входа в блок переключателей. По умолчанию остальная часть кода будет выполнена, если нет явного выхода. Так что имеет смысл только использовать одну и ту же область.
(Более фундаментально. Goto не структурированы - они не определяют или не разграничивают разделы кода, они только определяют точки перехода. Поэтому метка goto не может вводить область.)
C # сохраняет синтаксис, но вводит защиту от «провала», требуя выхода после каждого (не пустого) предложения case. Но это меняет наше представление о переключателе! Случаи теперь представляют собой альтернативные ветви , как ветви в if-else. Это означает, что мы ожидаем, что каждая ветвь будет определять свою собственную область видимости, как if-предложения или предложения итерации.
Итак, вкратце: дела имеют одинаковую область действия, потому что это то же самое, что и в C. Но это кажется странным и непоследовательным в C #, потому что мы рассматриваем случаи как альтернативные ветви, а не как цели goto.
источник
Упрощенный способ просмотра области видимости заключается в рассмотрении области видимости по блокам
{}
.Поскольку
switch
не содержит никаких блоков, он не может иметь разные области видимости.источник
for (int i = 0; i < n; i++) Write(i); /* legal */ Write(i); /* illegal */
? Там нет блоков, но есть разные области применения.for
оператором.switch
не создает блоки для каждого случая, только на верхнем уровне. Сходство в том, что каждый оператор создает один блок (считаяswitch
оператор).case
?case
что не содержит блок, если только вы не решите добавить его.for
длится до следующего оператора, если вы не добавите{}
его, поэтому он всегда имеет блок, ноcase
длится до тех пор, пока что-то не заставит вас покинуть реальный блок,switch
оператор.