Чому було твердження (j ++); заборонено?


169

Наступний код невірний (див. Його на ideone ):

public class Test
{
    public static void Main()
    {
        int j = 5;
        (j++);      // if we remove the "(" and ")" then this compiles fine.
    }
}

помилка CS0201: Лише призначення, виклик, приріст, зменшення, очікування та нові об'єкти вирази можуть використовуватися як оператор

  1. Чому код складається, коли ми видаляємо круглі дужки?
  2. Чому він не компілюється з дужками?
  3. Чому C # був розроблений саме так?

29
@ Сервіз багато людей, які займаються дизайном мови C #, знаходяться на ТАК, тож чому я запитав. Щось з цим не так?
user10607

34
Я не можу уявити собі більше тематичного питання, ніж "чому конкретна мова програмування таким способом обробляє таку поведінку?"
Маг Сі

20
Тепер у нас є відповідь Еріка Ліпперта. Можливо, ви хочете переглянути своє рішення щодо прийнятої відповіді. Це Різдво, тому, можливо, ви прийняли відповідь занадто рано.
Томас Веллер

17
@Servy Ви маєте на увазі, що дизайнерські рішення ніколи не документально підтверджені та абсолютно невідомі всім іншим? Він не просить користувачів SO вгадати, він просить відповіді - це не означає, що він просить здогадуватися. Чи люди відповідають на здогадки, це на них , а не на них . І як OP вказав, там є люди на переповнення стека , які зробили на самому ділі роботи на C # і з цих рішень.
Роб

5
@Rob: Проблема в тому, що так, якщо обґрунтування вже десь не задокументоване, воно повинно бути, але спекуляція весела і хто не хоче ділитися своїм? Якщо ви знайдете спосіб переконатися, що такі чисті (або "освічені") здогадки надійно збиті, скажіть мені, і ми складемо цілий капітал.
Дедуплікатор

Відповіді:


221

Глибокі розуміння високо оцінені.

Я зроблю все можливе.

Як відзначали інші відповіді, компілятор, що відбувається тут, виявляє, що вираз використовується як вираз . У багатьох мовах - C, JavaScript та багатьох інших - цілком законно використовувати вираз як вислів. 2 + 2;є законним цими мовами, хоча це твердження, яке не має ніякого ефекту. Деякі вирази корисні лише для своїх значень, деякі вирази корисні лише для їх побічних ефектів (наприклад, заклик до методу повернення недійсних даних), а деякі вирази, на жаль, корисні для обох. (Як приріст.)

Справа в тому, що: висловлювання, що складаються лише з виразів, майже напевно є помилками, якщо, як правило, ці вирази вважаються більш корисними для їх побічних ефектів, ніж їх значення . Дизайнери C # хотіли знайти середину, дозволяючи вирази, які, як правило, вважалися як побічні, одночасно забороняючи ті, які також вважаються корисними для їх значень. Набір виразів, які вони ідентифікували в C # 1.0, були приростами, скороченнями, викликами методів, призначеннями та дещо суперечливими викликами конструктора.


ВІДПОВІДЬ: Як правило, людина вважає, що конструкція об'єкта використовується як значення, яке воно виробляє, а не для побічного ефекту конструкції; на мою думку, це дозволяє new Foo();трохи невдало. Зокрема, я бачив цю закономірність у реальному коді, що спричинив дефект безпеки:

catch(FooException ex) { new BarException(ex); } 

Цей дефект напрочуд важко помітити, якщо код складний.


Таким чином, компілятор працює над тим, щоб виявити всі висловлювання, що складаються з виразів, яких немає у цьому списку. Зокрема, викладені в дужках вирази ідентифікуються як саме це - виразки в дужках. Їх немає у списку "дозволено як вирази висловлювань", тому їх заборонено.

Все це відповідає принципу дизайну мови C #. Якщо ви ввели текст, (x++);ви, ймовірно, робили щось не так . Це, мабуть, помилка друку M(x++);чи якась справедлива річ. Пам'ятайте, що ставлення команди компілятора C # не полягає в тому, "чи можемо ми знайти якийсь спосіб зробити цю роботу? " Ставлення команди компілятора C # таке: " якщо правдоподібний код виглядає як ймовірна помилка, повідомляємо розробника ". C # розробникам подобається таке ставлення.

Тепер, все , що сказав, є на самому ділі кілька дивних випадків , коли C # специфікація робить передбачають або стан безпосередньо , що круглі дужки заборонені , але C # компілятор дозволяє їм в будь-якому випадку. Майже у всіх цих випадках незначна невідповідність між вказаною поведінкою та дозволеною поведінкою є абсолютно нешкідливою, тому автори-компілятори ніколи не виправляли цих маленьких помилок. Про них можна прочитати тут:

Чи є різниця між поверненням myVar проти повернення (myVar)?


5
Що стосується: "Зазвичай, виклик конструктора використовується як значення, яке воно виробляє, а не для побічного ефекту побудови; на мою думку, це є деякою помилкою": я б уявив, що це один фактор у цьому рішенні було те , що якщо компілятор заборонив його, не було б ніякої чистої виправити в тих випадках , коли будуть закликають його побічний ефект. (Більшість інших висловлювань із забороненим виразом мають сторонні частини, які не служать ніякій меті, і їх можна видалити, за винятком випадків ... ? ... : ..., коли виправлення потрібно використовувати if/ elseзамість цього).
ruakh

1
@ruakh: Оскільки це поведінка, яку слід не рекомендувати, чисте виправлення не потрібно, якщо існує досить дешеве / просте виправлення. Який є; призначити його змінній і не використовувати цю змінну. Якщо ви хочете стати очевидним, що ви не використовуєте його, дайте цьому рядку коду його власну область. Хоча з технічної точки зору можна стверджувати, що це змінює семантику коду (марне призначення посилань все ще є марним призначенням посилань), на практиці це навряд чи має значення. Я визнаю, що він дійсно генерує дещо інший ІЛ ..., але лише якщо його складено без оптимізацій.
Брайан

Safari, Firefox та Chrome надають ReferenceError під час введення тексту contineu;.
Брайан Маккотчон

2
@McBrainy: Ти маєш рацію. Я неправильно пам'ятаю про дефект, що виникає внаслідок неправильної написання; Я перевірю свої замітки. Тим часом я видалив заяву, яка не ображає, оскільки вона не є більшою для цього.
Ерік Ліпперт

1
@Mike: Я згоден. Інша ситуація, в якій ми іноді бачимо твердження, - це тестові випадки, try { new Foo(null); } catch (ArgumentNullException)...але, очевидно, ці ситуації за визначенням не є виробничим кодом. Здається розумним, що такий код можна було записати для присвоєння фіктивній змінній.
Ерік Ліпперт

46

У специфікації мови C #

Висловлення виразів використовуються для оцінки виразів. Вирази, які можуть бути використані як оператори, включають виклики методів, виділення об'єктів за допомогою нового оператора, призначення з використанням = і операторів присвоєння складених даних, операції збільшення та зменшення з використанням операторів ++ і - та очікують виразів.

Поставлення дужок навколо оператора створює новий так званий вираз у дужках. З специфікації:

Вираз у дужках складається з виразу, укладеного в круглі дужки. ... Вираз в круглих дужках оцінюється виразом в дужках. Якщо вираз у круглих дужках позначає простір імен чи тип, виникає помилка часу компіляції. В іншому випадку результат дужки виразів є результатом оцінки виразного вмісту.

Оскільки круглі вирази не перераховані як допустимий вираз виразу, це не є дійсним твердженням відповідно до специфікації. Чому дизайнери вирішили зробити це таким чином - це хтось здогадується, але я думаю, що дужки не роблять корисної роботи, якщо все твердження міститься в дужках: stmtі (stmt)абсолютно однакові.


4
@Servy: Якщо ви уважно читаєте, це було трохи більш нюансовано, ніж це. Так звана "заява про вираження" дійсно є дійсним твердженням, а специфікація перераховує типи виразів, які можуть бути дійсними твердженнями. Однак я повторюю із специфікації, що тип виразу, який називається "вираз в дужках", НЕ в переліку дійсних виразів, які можна використовувати як дійсні вирази виразів.
Френк Брайс

4
Про OPмовний дизайн нічого не задають. Він просто хоче знати, чому це помилка. Відповідь: тому що це неправдиве твердження .
Леандро

9
Гаразд, моя погана. Відповідь: тому що, згідно специфікації , це не є дійсним твердженням. Ось у вас це: причина дизайну !
Леандро

3
Питання не задається глибокою, керованою дизайном причиною того, чому мова була створена для обробки цього синтаксису таким чином - він запитує, чому один фрагмент коду збирається, коли інший фрагмент коду (який для початківців виглядає так, як він повинен поводитися однаково) не вдається компілювати. Цей пост відповідає на це.
Маг Сі

4
@Servy JohnCarpenter не просто каже "Ви не можете цього зробити". Він каже, що дві речі, з якими вас бентежили, не однакові. j ++ - вираз вираження, тоді як (j ++) - вираз у дужках. Раптом зараз ОП знає різницю і в чому вони полягають. Це гарна відповідь. Він відповідає тілу його питання, а не заголовку. Я думаю, що в заголовку питання виникає велика суперечка від слова "дизайн", але на це дизайнери не повинні відповідати, лише зібравшись із специфікацій.
thinklarge

19

через те, що дужки навколо i++створюють / визначають вираз .. як говориться в повідомленні про помилку .. простий вираз не може використовуватися як вираз.

чому мова була розроблена таким чином? щоб запобігти помилкам, що мають оманливі вирази як заяви, які не створюють побічних ефектів, як, наприклад, код

int j = 5;
j+1; 

другий рядок не має ефекту (але ви, можливо, цього не помітили). Але замість того, щоб компілятор видалив його (оскільки код не потрібен) .it явно просить вас видалити його (щоб ви були в курсі або помилка) АБО виправити це, якщо ви забули щось набрати.

редагувати :

щоб зробити більш чіткою частину про Bracked .. дужки в c # (крім інших застосувань, як, наприклад, виклик функцій та викликів функцій), використовуються для групування виразів та повернення єдиного виразу (make of sub вирази).

на цьому рівні коду дозволені лише позиції .. так

j++; is a valid statement because it produces side effects

але, використовуючи прикріплений, ви перетворюєте це на вираз

myTempExpression = (j++)

і це

myTempExpression;

не вірно, тому що компілятор не може запевнити, що вираз як побічний ефект. (не без виникнення проблеми зупинки) ..


Так, я теж спочатку думав. Але це дійсно має побічний ефект. Він виконує пост-приріст jзмінної права?
user10607

1
j++;є дійсним твердженням, але, використовуючи дужки, які ви говорите let me take this stement and turn it into an expression.. і вирази не є дійсними в цьому пункті коду
CaldasGSM

Це дуже погано, що немає форми заяви "обчислити і проігнорувати", оскільки бувають випадки, коли код може стверджувати, що значення можна обчислити, не викидаючи виняток, але не піклуючись про обчислене таким чином фактичне значення. Оператор обчислення і ігнорування не використовувався б дуже страшно, але в таких випадках зрозумів би намір програміста.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.