Чому 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: Це здається елітарним ставленням до високих брів. У людей завжди виникають питання, і якщо немає причин запитувати, чому, тоді немає сенсу мати СО. Цей сайт призначений не стільки для того, щоб люди вирішували наші негайні проблеми (хоча це трапляється), скільки для того, щоб забезпечити сховище питань та відповідей, які допоможуть нам усім стати кращими програмістами. ТАК це питання про ЧОМУ! :-)
Річард,

Відповіді:


90

У контексті, який ви даєте, значення не має. Запис постійного рядка на консоль буде працювати однаково в будь-якому місці потоку програм. 1

Натомість ви зазвичай використовуєте їх для обмеження сфери дії деяких локальних змінних. Це додатково детально розроблено тут і тут . Подивіться на відповідь Жоао Анджело і відповідь Кріса Уолліса на прикладах коротких. Я вважаю, що те саме стосується деяких інших мов із синтаксисом у стилі С, але не те, що вони мали б відношення до цього питання.


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
У моєму словнику '(' = дужки, '{' = дужка, '[' = квадратні дужки
стор.

Зачекай, я б назвав їх фігурними дужками, і Вікіпедія включає це найменування. Оксфордський словник англійської мови визначає таке використання дужки як: 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 Це саме те, що є фігурними дужками на мовах стилю С - так званий "складний вислів".
Michael Ekstrand

12

Загальним правилом мов C-синтаксису є: "все, що між ними, { }повинно розглядатися як одне твердження, і воно може переходити скрізь, де тільки може одне твердження":

  • Після if.
  • Після a for, whileабо do.
  • У будь-якому місці коду .

Для всіх намірів і цілей, це як граматика мови включає це:

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

Тобто, "висловлювання може складатися з (різних речей) або початкової фігурної дужки, за якою слід список висловлювань (який може включати одне або декілька тверджень), після чого закрита фігурна дужка". IE " { }блок може замінити будь-яке твердження, де завгодно". В тому числі в середині коду.

Якщо не допустити { }блокування в будь-якому місці, де може йти одне твердження, насправді було б ускладнено визначення мови .


Хтось просто покращив цю відповідь (через 9 років), яка насправді не була конкретною відповіддю на це питання, а набагато більше загальною дискусією. Можливо, це може бути замінено мовою та бібліотеками .. Але все одно це було приємно, дякую.
Массімо

1

Оскільки C ++ (і java) допускає блокування коду без попереднього твердження.

C ++ дозволив їм, оскільки це зробив C.

Можна сказати, все зводиться до того, що дизайн програмної мови США (на основі С) виграв, а не європейської мовної програми (на основі Модули-2 ).

(Керуючі оператори діють на одне твердження, оператори можуть бути групами для створення нових операторів)


Ви маєте на увазі Module2, або Modula2?
Richard Ev

Дійсно, це правильна відповідь. Далі, я просто відповів би "Тому що кожна комп’ютерна мова це робить".
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.