web-dev-qa-db-ger.com

Warum war die Anweisung (j ++); verboten?

Der folgende Code ist falsch (siehe auf ideone ):

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

fehler CS0201: Nur Zuweisung, Aufruf, Inkrementierung, Dekrementierung, Warten und neue Objektausdrücke können als Anweisung verwendet werden

  1. Warum wird der Code kompiliert, wenn wir die Klammern entfernen?
  2. Warum kompiliert es nicht mit den Klammern?
  3. Warum wurde C # so entwickelt?
169
user10607

Tiefe Einsichten werden geschätzt.

Ich werde mein Bestes geben.

Wie andere Antworten festgestellt haben, stellt der Compiler hier fest, dass ein Ausdruck als Anweisung verwendet wird. In vielen Sprachen - C, JavaScript und vielen anderen - ist es vollkommen legal, einen Ausdruck als Aussage zu verwenden. 2 + 2; Ist in diesen Sprachen legal, obwohl dies eine Aussage ist, die keine Wirkung hat. Einige Ausdrücke sind nur für ihre Werte nützlich, einige Ausdrücke sind nur für ihre Nebenwirkungen nützlich (z. B. ein Aufruf einer Methode zum Zurückgeben von ungültigen Werten), und einige Ausdrücke sind leider für beide nützlich. (Wie inkrementieren.)

Der springende Punkt ist: Aussagen, die nur aus Ausdrücken bestehen, sind mit ziemlicher Sicherheit Fehler es sei denn, diese Ausdrücke werden normalerweise als nützlicher für ihre Nebenwirkungen angesehen als für ihre Werte. C # -Designer wollten einen Mittelweg finden, indem sie Ausdrücke zuließen, die im Allgemeinen als nebenwirkend angesehen wurden, während sie Ausdrücke, die normalerweise auch als nützlich für ihre Werte angesehen werden, nicht zuließen. Die Menge der Ausdrücke, die sie in C # 1.0 identifizierten, waren Inkremente, Dekremente, Methodenaufrufe, Zuweisungen und etwas umstrittene Konstruktoraufrufe.


ASIDE: Man denkt normalerweise an eine Objektkonstruktion, die für den Wert verwendet wird, den sie erzeugt, nicht für die Nebenwirkung der Konstruktion; Meiner Meinung nach ist das Zulassen von new Foo(); ein Misserfolg. Insbesondere habe ich dieses Muster im Code der realen Welt gesehen, das einen Sicherheitsmangel verursacht hat:

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

Es kann überraschend schwierig sein, diesen Fehler zu erkennen, wenn der Code kompliziert ist.


Der Compiler erkennt daher alle Anweisungen, die aus Ausdrücken bestehen, die nicht in dieser Liste enthalten sind. Insbesondere werden in Klammern gesetzte Ausdrücke als solche identifiziert - in Klammern gesetzte Ausdrücke. Sie sind nicht in der Liste "als Anweisungsausdrücke zulässig" enthalten, daher sind sie nicht zulässig.

All dies dient einem Entwurfsprinzip der C # -Sprache. Wenn Sie (x++); Eingegeben haben, haben Sie wahrscheinlich etwas falsch gemacht . Dies ist wahrscheinlich ein Tippfehler für M(x++); oder eine einfache Sache. Denken Sie daran, die Haltung des C # -Compiler-Teams ist nicht " können wir einen Weg finden, um dies zum Laufen zu bringen?" Die Haltung des C # -Compiler-Teams ist " wenn plausibler Code aussieht wie ein wahrscheinlicher Fehler, informieren wir den Entwickler ". C # -Entwickler mögen diese Einstellung.

Nun, alles in allem gibt es tatsächlich ein paar seltsame Fälle, in denen die C # -Spezifikation does impliziert oder festlegt, dass Klammern nicht zulässig sind, der C # -Compiler sie jedoch trotzdem zulässt. In fast allen diesen Fällen ist die geringfügige Diskrepanz zwischen dem angegebenen Verhalten und dem zulässigen Verhalten völlig harmlos, sodass die Compiler-Autoren diese kleinen Fehler nie behoben haben. Darüber können Sie hier lesen:

Gibt es einen Unterschied zwischen return myVar und return (myVar)?

220
Eric Lippert

In der C # -Sprachenspezifikation

Ausdrucksanweisungen werden zum Auswerten von Ausdrücken verwendet. Ausdrücke, die als Anweisungen verwendet werden können, umfassen Methodenaufrufe, Objektzuweisungen mit dem neuen Operator, Zuweisungen mit den Operatoren = und der zusammengesetzten Zuweisung, Inkrementierungs- und Dekrementierungsoperationen mit den Operatoren ++ und - und warten auf Ausdrücke.

Wenn Sie eine Anweisung in Klammern setzen, wird ein neuer Ausdruck in Klammern erstellt. Aus der Spezifikation:

Ein Ausdruck in Klammern besteht aus einem Ausdruck in Klammern. ... Ein Ausdruck in Klammern wird ausgewertet, indem der Ausdruck in Klammern ausgewertet wird. Wenn der Ausdruck in Klammern einen Namespace oder Typ angibt, tritt ein Fehler beim Kompilieren auf. Andernfalls ist das Ergebnis des Ausdrucks in Klammern das Ergebnis der Auswertung des enthaltenen Ausdrucks.

Da in Klammern gesetzte Ausdrücke nicht als gültige Ausdrucksanweisung aufgeführt sind, handelt es sich nicht um eine gültige Anweisung gemäß der Spezifikation. Warum sich die Designer für diese Methode entschieden haben, ist unklar, aber meine Wette ist, dass Klammern keine sinnvolle Arbeit leisten, wenn die gesamte Aussage in Klammern steht: stmt und (stmt) sind genau das gleiche.

46
Frank Bryce

weil die Klammern um i++ einen Ausdruck erstellen/definieren ... wie die Fehlermeldung besagt ... kann ein einfacher Ausdruck nicht als Anweisung verwendet werden.

warum wurde die Sprache so gestaltet? um zu verhindern, dass Fehler, die irreführende Ausdrücke als Anweisungen enthalten, keine Nebenwirkungen hervorrufen wie der Code

int j = 5;
j+1; 

die zweite Zeile hat keine Auswirkung (aber Sie haben es möglicherweise nicht bemerkt). Anstatt dass der Compiler es entfernt (weil der Code nicht benötigt wird), werden Sie explizit aufgefordert, es zu entfernen (damit Sie den Fehler bemerken) OR, falls Sie vergessen haben, etwas einzugeben.

bearbeiten:

um den Teil über Klammern klarer zu machen, werden Klammern in c # (neben anderen Verwendungen wie cast und function call) verwendet, um Ausdrücke zu gruppieren und einen einzelnen Ausdruck zurückzugeben (make der Unterausdrücke).

auf dieser Code-Ebene sind nur Stamente erlaubt

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

aber mit der Klammer verwandelst du sie in einen Ausdruck

myTempExpression = (j++)

und das

myTempExpression;

ist nicht gültig, da der Compiler nicht sicher stellen kann, dass der Ausdruck ein Nebeneffekt ist (nicht ohne das Problem des Anhaltens zu haben).

19
CaldasGSM