Почему C # допускает блоки кода {} без предшествующего оператора?


86

Почему C # позволяют блоки кода без предшествующего заявления (например if, else, for, while)?

void Main()
{
    {   // any sense in this?
        Console.Write("foo");
    }
}

70
Есть ли причина, по которой этого не должно быть ?
Jamiec

30
Но, как показывают ответы, это имеет большее значение, чем просто «не повредит, так что позволь». Вот и смысл задавать такие вопросы.
Селдон

8
+1 вопрос звучит достаточно невинно, но ответы действительно научили меня чему-то ценному
Nicolas78

7
@Akash: Не спрашивая почему, мы никогда не поправимся.
Ричард

7
@Akash: Это похоже на высокомерную элитарную позицию. У людей всегда есть вопросы, и если нет причин спрашивать, почему, тогда нет смысла иметь SO. Этот сайт предназначен не столько для того, чтобы люди решали наши насущные проблемы (хотя это действительно происходит), но для предоставления хранилища вопросов и ответов, которые помогут нам всем стать лучшими программистами. ТАК, чтобы спросить ПОЧЕМУ! :-)
richard

Ответы:


90

В контексте, который вы даете, это не имеет значения. Запись постоянной строки в консоль будет работать одинаково в любом месте выполнения программы. 1

Вместо этого вы обычно используете их для ограничения области некоторых локальных переменных. Подробнее об этом здесь и здесь . Посмотрите на ответ Жоао Анджело и ответ Криса Уоллиса на примерах коротких. Я считаю, что то же самое относится и к некоторым другим языкам с синтаксисом в стиле C, хотя они не будут иметь отношения к этому вопросу.


1 Если, конечно, вы не решите пошутить и создать свой собственный Consoleкласс с Write()методом, который делает что-то совершенно неожиданное.


24
Re: другие языки: обычно, но не всегда. JavaScript - заметное исключение; объявление локальной переменной в конкретном блоке не распространяется на эту переменную блока.
Эрик Липперт

@EricLippert: В C # объявление переменной внутри блока приводит к изменению области видимости переменной с точки зрения времени выполнения? Насколько я могу судить, время жизни переменных часто не ограничивается блоками области видимости, факт, который наблюдается в проектах со смешанным языком, если первое, что делается с переменной в цикле, - это передать ее в качестве outпараметра в реализацию интерфейса, написанную на другом языке, и эта реализация считывает переменную перед ее записью.
supercat

А в C ++ из-за RAII вместо GC это намного полезнее.
Дедупликатор

В C ++ он действует подобно блоку try / catch, поэтому переменные и классы, объявленные в этой области видимости, выпадают из стека, когда вы покидаете эту область. Это помогает предотвратить конфликты имен и сокращает объем памяти, занимаемой функцией. Он также удаляет эти переменные из автозаполнения редактора кода. Часто я нахожу блоки try / catch более полезными.
Kit10

144

По { ... }крайней мере, побочный эффект заключается в введении новой области видимости для локальных переменных.

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



6
Если вам нужны прицелы в ваших чемоданах, они слишком велики! Метод извлечения.
Джей Базузи

20
@Jay Bazuzi, просто потому, что я хочу иметь локальную переменную с тем же именем более чем в одном случае, не означает, что они должны быть огромными. Вы просто делаете огромные выводы ... :)
Жуан Анджело

Поздравляем с получением значка "Отличный ответ"! :)
BoltClock

1
@BoltClock, спасибо, мне просто нужно еще несколько вопросов, {}чтобы сохранить эти золотые значки ... :)
Жуан Анджело

56

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

В вашем примере фигурные скобки вообще не действуют, но в следующем коде они определяют область видимости и, следовательно, видимость переменной:

Этот разрешено, поскольку i выходит за рамки первого блока и снова определяется в следующем:

{
    {
        int i = 0;
    }

    {
        int i = 0;
    }
}

Этот недопустимо, поскольку i выпал из области видимости и больше не отображается во внешней области:

{
    {
        int i = 0;
    }

    i = 1;
}

И так далее, и так далее.


1
Я читал о различиях в номенклатуре скобок в различных вариантах английского языка, но в каких именно {}скобках?
BoltClock

Хороший вопрос! Думаю, один наставник вложил это в мою голову десять лет назад. Наверное, мне следовало бы называть их «фигурные скобки» или «фигурные скобки». Каждому свое ... en.wikipedia.org/wiki/…
Крис Уоллис,

10
В моем словаре '(' = скобка, '{' = скобка, '[' = квадратные скобки
pb.

Подождите, я бы назвал их фигурными скобками, и Википедия включает это название. Оксфордский словарь английского языка определяет это использование скобок как: « One of two marks of the form [ ] or ( ), and in mathematical use also {}, used for enclosing a word or number of words, a portion of a mathematical formula, or the like, so as to separate it from the context; В любом случае это не скобки, но« фигурные скобки »кажутся нормальными.
dumbledad

IME, «фигурные скобки» и «квадратные скобки».
cp.engr

18

я полагаю {} как утверждение, которое может содержать несколько утверждений.

Рассмотрим оператор if, который существует из логического выражения, за которым следует один оператор. Это сработает:

if (true) Console.Write("FooBar");

Это тоже сработает:

if (true)
{
  Console.Write("Foo");
  Console.Write("Bar");
}

Если я не ошибаюсь, это называется блок-оператором.

Поскольку {}может содержать другие операторы, он также может содержать другие {}. Объем переменной определяется ее родителем{} (оператором блока).

Я пытаюсь {}сказать, что это просто утверждение, поэтому оно не требует if или чего-то еще ...


+1 Именно это и есть фигурные скобки в языках C-стиля - так называемый «составной оператор».
Майкл Экстранд

12

Общее правило в языках синтаксиса C гласит: «Все, что находится между ними, { }следует рассматривать как один оператор, и оно может идти везде, где может выполнить один оператор»:

  • После if.
  • После а for, whileили do.
  • В любом месте кода .

Во всех смыслах и целях грамматика языка включала следующее:

     <statement> :== <definition of valid statement> | "{" <statement-list> "}"
<statement-list> :== <statement> | <statement-list> <statement>

То есть «оператор может состоять из (различных вещей) или открывающей скобки, за которой следует список операторов (который может включать одно или несколько операторов), за которым следует закрытая скобка». ИП "а{ } блок может заменить любой оператор в любом месте». В том числе и в середине кода.

Если { }запретить блокировку в любом месте, куда может идти одна инструкция, определение языка на самом деле усложнилось бы .


Кто-то только что повысил этот ответ (через 9 лет), который на самом деле не был конкретным ответом на этот вопрос, а скорее общим обсуждением. Возможно, он был заменен языком и библиотеками ... Но все равно было приятно, спасибо.
Массимо,

1

Поскольку C ++ (и java) допускает блоки кода без предшествующего оператора.

C ++ позволил им, потому что это сделал C.

Можно сказать, что все сводится к тому, что дизайн на американском языке программ (на основе C) победил, а не на европейском языке программирования (на основе Modula-2 ).

(Управляющие операторы действуют на один оператор, операторы могут быть группами для создания новых операторов)


Вы имеете в виду Module2 или Modula2?
Ричард Ев

Действительно, это правильный ответ. Далее, я бы просто ответил: «Потому что каждый компьютерный язык умеет».
Fattie


0

1 Потому что ... Это поддерживает область действия оператора ... или функции, это действительно полезно для работы с большим кодом ..

{
    {
        // Here this 'i' is we can use for this scope only and out side of this scope we can't get this 'i' variable.

        int i = 0;
    }

    {
        int i = 0;
    }
}

введите описание изображения здесь


0

Вы спросили, «почему» C # допускает блоки кода без предшествующих операторов. Вопрос «почему» можно также интерпретировать как «каковы были бы возможные преимущества этой конструкции?»

Лично я использую блоки кода без инструкций в C #, где читаемость значительно улучшена для других разработчиков, при этом помня, что блок кода ограничивает область действия локальных переменных. Например, рассмотрим следующий фрагмент кода, который намного легче читать благодаря дополнительным блокам кода:

OrgUnit world = new OrgUnit() { Name = "World" };
{
    OrgUnit europe = new OrgUnit() { Name = "Europe" };
    world.SubUnits.Add(europe);
    {
        OrgUnit germany = new OrgUnit() { Name = "Germany" };
        europe.SubUnits.Add(germany);

        //...etc.
    }
}
//...commit structure to DB here

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

Таким образом, несмотря на то, что приведенный выше код выполняется линейно, структура кода представляет собой «реальную» структуру объектов, что упрощает другим разработчикам понимание, поддержку и расширение.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.