なぜステートメントだったのですか(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
@Servy C#言語設計に関わる多くの人がSOにいるので、私が尋ねたのはそのためです。これに問題はありますか?
user10607

34
「なぜ特定のプログラミング言語がこの特定の振る舞いをこのように処理するのですか?」以上の話題の質問を想像することはできません。
メイジXY

20
さて、エリック・リペルトの答えがあります。多分あなたは受け入れられた答えについてのあなたの決定を再考したいと思うでしょう。それはクリスマスなので、おそらくあなたは答えを早く受け入れすぎました。
Thomas Weller

17
@Servy設計上の決定が文書化されることはなく、誰にとっても絶対に知られていないことを意味していますか?彼 SOユーザーに推測を求めているのではなく、答えを求めている-それは彼が推測を求めていることを意味するものではない。人々が推測で答えるかどうかは彼ではなく彼らにあります。OPが指摘したように、そこにあるスタックオーバーフロー上の人でした C#上で、実際に仕事をし、これらの決定をしたが。
ロブ

5
@Rob:問題は、はい、理論的根拠がすでにどこかで文書化されていない限り、それはそうでなければなりませんが、推測は楽しいものであり、誰が彼自身のものを共有したくないのですか?そのような純粋な(または「教育された」)推測が確実に間引かれることを確認する方法を見つけたら、教えてください。
Deduplicator 2015

回答:


221

深い洞察を感謝します。

がんばります。

他の回答が指摘しているように、ここで起こっているのは、ステートメントとして使用されていることをコンパイラが検出していることです。C、JavaScript、その他多くの言語では、式をステートメントとして使用することは完全に合法です。 2 + 2;これは効果のないステートメントですが、これらの言語では合法です。一部の式は値にのみ有効であり、一部の式は副作用(voidを返すメソッドの呼び出しなど)にのみ有効であり、一部の式は残念ながら両方に役立ちます。(増分のように。)

要点:式のみで構成されるステートメントは、それらの式が通常、値よりも副作用に役立つと考えられていない限り、ほぼ間違いなくエラーです。C#の設計者は、一般に副作用と考えられている式を許可する一方で、通常はその値に役立つと考えられている式を許可しないことで、中間点を見つけたいと考えました。C#1.0で識別された一連の式は、インクリメント、デクリメント、メソッド呼び出し、割り当て、そして議論の余地があるが、コンストラクターの呼び出しでした。


ASIDE:通常、オブジェクトの構造は、その構造の副作用ではなく、生成する値に使用されると考えられます。私の意見でnew Foo();は、許可することは少し誤動作です。特に、セキュリティの欠陥を引き起こしたこのパターンを実際のコードで確認しました。

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

コードが複雑な場合、この欠陥を見つけるのは驚くほど難しい場合があります。


したがって、コンパイラーは、そのリストにない式で構成されるすべてのステートメントを検出するように機能します。特に、括弧で囲まれた式は、括弧で囲まれた式として識別されます。これらは「ステートメント式として許可」のリストにないため、許可されません。

これらはすべて、C#言語の設計原則に基づいています。入力した(x++);場合、おそらく何か間違ったことをしていたでしょう。これは、おそらくタイプミスM(x++);か、それとも単なる誤りです。覚えておいてください、C#コンパイラチームの態度ではない「私たちは、この作品を作るためにいくつかの方法を考え出すことができますか?」C#コンパイラチームの姿勢「である可能性が間違いのようなもっともらしいコードに見える場合は、聞かせてのは、開発者に通知します」。C#開発者はその態度を好みます。

とはいえ、実際には、C#の仕様で括弧が許可されていないことを暗示または明示している奇妙なケースが実際にはいくつかありますが、C#コンパイラでは括弧を許可しています。これらのほとんどすべての場合、指定された動作と許可された動作のわずかな不一致は完全に無害であるため、コンパイラの作成者はこれらの小さなバグを修正していません。あなたはそれらについてここで読むことができます:

return myVarとreturn(myVar)に違いはありますか?


5
Re:「通常、コンストラクターの呼び出しは、それが生成する値に使用されていると考えています。構造の副作用ではありません。私の意見では、これは少し誤った機能です」:この決定の1つの要素を想像します。コンパイラはそれを禁じている場合、あなたは例のいずれかのきれいな修正がないことだったされている副作用のためにそれを呼び出します。(他のほとんどの禁止された式ステートメントには、目的を果たさない余分な部分があり、削除することができます... ? ... : ...。ただし、if/ を使用する場合は修正が必要elseです)
ruakh

1
@ruakh:これは推奨されない動作であるため、妥当なコストで簡単な修正がある限り、クリーンな修正は必要ありません。あります。それを変数に割り当て、その変数を使用しないでください。使用していないことを露骨にしたい場合は、そのコード行に独自のスコープを指定してください。技術的には、これによってコードのセマンティクスが変わると主張することもできますが(役に立たない参照の割り当てはまだ役に立たない参照の割り当てです)、実際には問題になることはほとんどありません。私はそれがわずかに異なるILを生成することを認めます...しかし、最適化なしでコンパイルされた場合のみ。
ブライアン、

Safari、Firefox、およびChromeはすべて、を入力するとReferenceErrorを返しますcontineu;
Brian McCutchon 2015

2
@McBrainy:その通りです。スペルミスに起因する欠陥を覚えています。メモを確認します。その間、ここでより大きなポイントに密接に関係していないので、問題のあるステートメントを削除しました。
Eric Lippert、2015

1
@マイク:同意する。ステートメントが時々見られるもう1つの状況は、テストケースのようなものですtry { new Foo(null); } catch (ArgumentNullException)...が、明らかにこれらの状況は、当然のことながら、製品コードではありません。このようなコードは、ダミー変数に割り当てるように記述できるのは妥当なことです。
Eric Lippert、2016年

46

ではC#言語仕様

式ステートメントは、式を評価するために使用されます。ステートメントとして使用できる式には、メソッドの呼び出し、new演算子を使用したオブジェクトの割り当て、=と複合代入演算子を使用した割り当て、++および-演算子を使用した増分演算と減分演算、およびawait式が含まれます。

ステートメントを括弧で囲むと、新しい括弧付き式が作成されます。仕様から:

括弧で囲まれた式は、括弧で囲まれた式で構成されます。...括弧で囲まれた式は、括弧内の式を評価することによって評価されます。括弧内の式が名前空間またはタイプを示している場合、コンパイル時エラーが発生します。それ以外の場合、括弧で囲まれた式の結果は、含まれている式の評価の結果です。

括弧で囲まれた式は有効な式ステートメントとしてリストされていないため、仕様によると有効なステートメントではありません。デザイナーがこの方法でこれを行うことを選択した理由は誰もが推測することですが、私の賭けは、ステートメント全体が括弧内に含まれている場合、括弧は役に立たないためでstmtあり(stmt)、まったく同じです。


4
@Servy:よく読むと、それよりも少し微妙な違いがありました。いわゆる「式ステートメント」は確かに有効なステートメントであり、仕様には有効なステートメントになり得る式のタイプがリストされています。ただし、「括弧で囲まれた式」と呼ばれる式のタイプは、有効な式ステートメントとして使用できる有効な式のリストにないという仕様から繰り返します。
フランク・ブライス、2015

4
OP言語設計について何も求めていません。彼は、なぜこれがエラーなのか知りたいだけです。答えは、有効なステートメントではないためです
Leandro

9
OK、悪い。答えは次のとおりです、ので、仕様に応じて、それが有効な文ではありません。設計の理由があります。
Leandro

3
問題は、言語がこのようにこの構文を処理するように設計された理由について、設計主導の深い理由を求めているのではありません。同様に)コンパイルに失敗します。この投稿はその答えです。
Mage Xy

4
@Servy JohnCarpenterは、「それはできない」と言っているだけではありません。彼が言っているのは、あなたが混乱したこれら二つのことは同じではないということです。j ++は式ステートメントであり、(j ++)は括弧で囲まれた式です。突然、OPは違いとそれらが何であるかを知った。それは良い答えです。タイトルではなく質問の本文に回答します。質問のタイトルにある「デザイン」という言葉から多くの論争が生じていると思いますが、これは設計者が答える必要はなく、仕様から収集したものです。
thinklarge

19

i++エラーメッセージが示すように、括弧で囲まれた式が式を作成/定義しているため、単純な式はステートメントとして使用できません。

言語がこのように設計されたのはなぜですか?誤解を招く表現をステートメントとして持ち、コードのような副作用を引き起こさないバグを防ぐため

int j = 5;
j+1; 

2行目は効果がありません(ただし、気付かなかった可能性があります)。しかし、コンパイラがそれを削除するのではなく(コードが必要ないため)、それを明示的に削除するように求めます(そうすることで、エラーまたはエラーに気づくでしょう)、または何か入力し忘れた場合に備えて修正します。

編集

括弧で囲まれた部分をより明確にするために.. c#の括弧(キャストや関数呼び出しなどの他の用途以外)は、式をグループ化し、単一の式(サブ式の作成)を返すために使用されます。

そのレベルのコードでは、ステートメントのみが許可されます。

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

コードが例外をスローせずに値を計算できると主張したいが、実際に計算された値は気にしない場合があるため、「計算して無視」ステートメントの形式がないのは残念です。計算して無視するステートメントはそれほど頻繁に使用されることはありませんが、そのような場合にはプログラマーの意図を明確にします。
スーパーキャット2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.