Dies ist erlaubt:
int a, b, c;
a = b = c = 16;
string s = null;
while ((s = "Hello") != null) ;
Nach meinem Verständnis sollte die Zuweisung s = ”Hello”;
nur dazu führen “Hello”
, dass sie zugewiesen wird s
, aber die Operation sollte keinen Wert zurückgeben. Wenn das wahr ((s = "Hello") != null)
wäre, würde das einen Fehler erzeugen, da null
es mit nichts verglichen würde.
Was ist der Grund dafür, dass Zuweisungsanweisungen einen Wert zurückgeben dürfen?
while ((s = GetWord()) != null) Process(s);
nicht).Antworten:
Ihr Verständnis ist 100% falsch. Können Sie erklären, warum Sie diese falsche Sache glauben?
Zunächst einmal, Zuordnung Anweisungen erzeugen keinen Wert. Zuordnung Ausdrücke erzeugen einen Wert. Ein Zuweisungsausdruck ist eine rechtliche Erklärung. Es gibt nur eine Handvoll Ausdrücke, die rechtliche Aussagen in C # sind: Warten auf einen Ausdruck, Instanzkonstruktion, Inkrementieren, Dekrementieren, Aufrufen und Zuweisen von Ausdrücken können verwendet werden, wenn eine Anweisung erwartet wird.
Es gibt nur eine Art von Ausdruck in C #, die keinen Wert erzeugt, nämlich einen Aufruf von etwas, das als zurückkehrende Leere eingegeben wird. (Oder gleichwertig das Warten auf eine Aufgabe ohne zugeordneten Ergebniswert.) Jede andere Art von Ausdruck erzeugt einen Wert oder eine Variable oder eine Referenz oder einen Eigenschaftszugriff oder einen Ereigniszugriff und so weiter.
Beachten Sie, dass alle Ausdrücke, die als Aussagen zulässig sind , für ihre Nebenwirkungen nützlich sind . Das ist die wichtigste Erkenntnis hier, und ich denke, vielleicht die Ursache Ihrer Intuition, dass Aufgaben Aussagen und keine Ausdrücke sein sollten. Im Idealfall hätten wir genau eine Nebenwirkung pro Aussage und keine Nebenwirkungen in einem Ausdruck. Es ist etwas seltsam, dass nebeneffektiver Code überhaupt in einem Ausdruckskontext verwendet werden kann.
Der Grund für das Zulassen dieser Funktion liegt darin, dass (1) sie häufig praktisch ist und (2) in C-ähnlichen Sprachen idiomatisch ist.
Man könnte bemerken, dass die Frage gestellt wurde: Warum ist dies in C-ähnlichen Sprachen idiomatisch?
Dennis Ritchie kann leider nicht mehr gefragt werden, aber ich vermute, dass eine Zuordnung fast immer den Wert hinterlässt, der gerade in einem Register zugewiesen wurde. C ist eine sehr "maschennah" Sprache. Es erscheint plausibel und im Einklang mit dem Design von C, dass es ein Sprachmerkmal gibt, das im Grunde bedeutet, "weiterhin den Wert zu verwenden, den ich gerade zugewiesen habe". Es ist sehr einfach, einen Codegenerator für diese Funktion zu schreiben. Sie verwenden einfach weiterhin das Register, in dem der zugewiesene Wert gespeichert ist.
quelle
=
/==
Tippfehler, der leicht behoben werden kann, indem der Wert von=
nicht zugelassen wird, es sei denn, er steht in Klammern. zBif ((x = y))
oderif ((x = y) == true)
ist erlaubt, aberif (x = y)
nicht.Hast du nicht die Antwort gegeben? Es soll genau die Arten von Konstrukten aktivieren, die Sie erwähnt haben.
Ein häufiger Fall, in dem diese Eigenschaft des Zuweisungsoperators verwendet wird, ist das Lesen von Zeilen aus einer Datei ...
quelle
return _myBackingField ?? (_myBackingField = CalculateBackingField());
Viel weniger Spreu als auf Null zu prüfen und zuzuweisen.Meine bevorzugte Verwendung von Zuweisungsausdrücken ist für träge initialisierte Eigenschaften.
quelle
??=
Syntax vorgeschlagen.Zum einen können Sie Ihre Aufgaben wie in Ihrem Beispiel verketten:
Zum anderen können Sie ein Ergebnis in einem einzigen Ausdruck zuweisen und überprüfen:
Beides sind möglicherweise zweifelhafte Gründe, aber es gibt definitiv Leute, die diese Konstrukte mögen.
quelle
return (HttpContext.Current.Items["x"] = myvar);
Abgesehen von den bereits genannten Gründen (Zuordnungsverkettung, Setzen und Testen innerhalb von while-Schleifen usw.) benötigen Sie diese Funktion , um die Anweisung richtig zu verwenden
using
:MSDN rät davon ab, das Einwegobjekt außerhalb der using-Anweisung zu deklarieren, da es auch nach seiner Entsorgung im Geltungsbereich bleibt (siehe den von mir verlinkten MSDN-Artikel).
quelle
using
. Alle diese sind legal:using (X x = new X())
,using (x = new X())
,using (x)
. In diesem Beispiel ist der Inhalt der using-Anweisung jedoch eine spezielle Syntax, die überhaupt nicht von der Zuweisung eines Werts abhängt. SieFont font3 = new Font("Arial", 10.0f)
ist kein Ausdruck und an keiner Stelle gültig, an der Ausdrücke erwartet werden.Ich möchte auf einen bestimmten Punkt eingehen, den Eric Lippert in seiner Antwort angesprochen hat, und einen besonderen Anlass ins Rampenlicht rücken, der von niemand anderem berührt wurde. Eric sagte:
Ich möchte sagen, dass die Zuweisung immer den Wert hinterlässt, den wir versucht haben, unserem linken Operanden zuzuweisen. Nicht nur "fast immer". Aber ich weiß es nicht, weil ich dieses Problem in der Dokumentation nicht kommentiert habe. Es könnte theoretisch ein sehr effektiv implementiertes Verfahren sein, den linken Operanden "zurückzulassen" und nicht neu zu bewerten, aber ist es effizient?
'Effizient' ja für alle Beispiele, die bisher in den Antworten dieses Threads erstellt wurden. Aber effizient bei Eigenschaften und Indexern, die get- und set-Accessoren verwenden? Überhaupt nicht. Betrachten Sie diesen Code:
Hier haben wir eine Eigenschaft, die nicht einmal ein Wrapper für eine private Variable ist. Wann immer er aufgefordert wird, wird er wahr zurückkehren, wann immer jemand versucht, seinen Wert festzulegen, wird er nichts tun. Wenn also diese Eigenschaft bewertet wird, muss er wahr sein. Mal sehen was passiert:
Ratet mal, was es druckt? Es wird gedruckt
Unexpected!!
. Wie sich herausstellt, wird der Set-Accessor tatsächlich aufgerufen, was nichts bewirkt. Danach wird der get accessor überhaupt nicht mehr aufgerufen. Die Zuordnung hinterlässt einfach denfalse
Wert, den wir versucht haben, unserem Eigentum zuzuweisen. Und dieserfalse
Wert wird von der if-Anweisung ausgewertet.Ich werde mit einem Beispiel aus der Praxis abschließen, das mich dazu gebracht hat, dieses Problem zu untersuchen. Ich habe einen Indexer erstellt, der ein praktischer Wrapper für eine Sammlung (
List<string>
) war, die eine Klasse von mir als private Variable hatte.Der an den Indexer gesendete Parameter war eine Zeichenfolge, die als Wert in meiner Sammlung behandelt werden sollte. Der get-Accessor würde einfach true oder false zurückgeben, wenn dieser Wert in der Liste vorhanden ist oder nicht. Daher war der get-Accessor eine andere Möglichkeit, die
List<T>.Contains
Methode zu verwenden.Wenn der Set-Accessor des Indexers mit einer Zeichenfolge als Argument aufgerufen wurde und der rechte Operand ein Bool war
true
, würde er diesen Parameter zur Liste hinzufügen. Wenn jedoch derselbe Parameter an den Accessor gesendet wurde und der richtige Operand ein Bool warfalse
, löschte er stattdessen das Element aus der Liste. Daher wurde der Set-Accessor als bequeme Alternative zuList<T>.Add
und verwendetList<T>.Remove
.Ich dachte, ich hätte eine ordentliche und kompakte "API", die die Liste mit meiner eigenen Logik umschließt, die als Gateway implementiert ist. Mit Hilfe eines Indexers allein konnte ich viele Dinge mit ein paar Tastenanschlägen erledigen. Wie kann ich beispielsweise versuchen, meiner Liste einen Wert hinzuzufügen und zu überprüfen, ob er dort enthalten ist? Ich dachte, dies sei die einzige notwendige Codezeile:
Aber wie mein früheres Beispiel gezeigt hat, wurde der get-Accessor, der sehen soll, ob der Wert wirklich in der Liste enthalten ist, nicht einmal aufgerufen. Der
true
Wert blieb immer zurück und zerstörte effektiv die Logik, die ich in meinem get accessor implementiert hatte.quelle
Wenn die Zuweisung keinen Wert zurückgeben würde, würde die Zeile
a = b = c = 16
auch nicht funktionieren.Auch Dinge wie schreiben zu
while ((s = readLine()) != null)
können, kann manchmal nützlich sein.Der Grund dafür, dass die Zuweisung den zugewiesenen Wert zurückgibt, besteht darin, dass Sie diese Dinge tun.
quelle
=
Auftreten in einem Ausdruck, der nicht in Klammern steht, nicht zugelassen oder gewarnt wird (ohne Berücksichtigung der Parens der if / while-Anweisung selbst). gcc gibt solche Warnungen aus und hat dadurch diese Klasse von Fehlern aus C / C ++ - Programmen, die damit kompiliert wurden, im Wesentlichen beseitigt. Es ist eine Schande, dass andere Compiler-Autoren diesem und einigen anderen guten Ideen in gcc so wenig Aufmerksamkeit geschenkt haben.Ich denke, Sie verstehen falsch, wie der Parser diese Syntax interpretieren wird. Die Zuordnung wird bewertet zuerst , und das Ergebnis wird dann auf NULL verglichen werden, dh die Aussage entspricht:
Wie andere bereits betont haben, ist das Ergebnis einer Zuweisung der zugewiesene Wert. Es fällt mir schwer, mir den Vorteil vorzustellen
((s = "Hello") != null)
und
nicht gleichwertig sein ...
quelle
Ich denke, der Hauptgrund ist die (absichtliche) Ähnlichkeit mit C ++ und C. Der Assigment-Operator (und viele andere Sprachkonstrukte) verhält sich wie seine C ++ - Gegenstücke und folgt nur dem Prinzip der geringsten Überraschung, und jeder Programmierer, der von einem anderen Curly- stammt. Bracket-Sprache kann sie verwenden, ohne viel nachzudenken. Für C ++ - Programmierer leicht zu erlernen, war eines der Hauptentwurfsziele für C #.
quelle
Aus den beiden Gründen, die Sie in Ihren Beitrag aufnehmen,
1) können Sie dies tun,
a = b = c = 16
2) um zu testen, ob eine Aufgabe erfolgreich war
if ((s = openSomeHandle()) != null)
quelle
Die Tatsache, dass 'a ++' oder 'printf ("foo")' entweder als in sich geschlossene Anweisung oder als Teil eines größeren Ausdrucks nützlich sein kann, bedeutet, dass C die Möglichkeit berücksichtigen muss, dass Ausdrucksergebnisse sein können oder nicht gebraucht. Angesichts dessen gibt es eine allgemeine Vorstellung, dass Ausdrücke, die einen Wert sinnvollerweise "zurückgeben" könnten, dies auch tun könnten. Die Zuordnungsverkettung kann in C leicht "interessant" und in C ++ sogar noch interessanter sein, wenn nicht alle fraglichen Variablen genau denselben Typ haben. Solche Verwendungen werden wahrscheinlich am besten vermieden.
quelle
Ein weiterer guter Anwendungsfall, den ich ständig benutze:
quelle
Ein zusätzlicher Vorteil, den ich in den Antworten hier nicht sehe, ist, dass die Syntax für die Zuweisung auf Arithmetik basiert.
Bedeutet jetzt
x = y = b = c = 2 + 3
etwas anderes in der Arithmetik als eine Sprache im C-Stil; in arithmetischer seine Behauptung, wir sagen , dass x zu y usw. , und in einer C-Stil Sprache gleich ist es ein Befehl ist , dass Marken x zu y usw. gleich nachdem er ausgeführt wird.Trotzdem gibt es immer noch genug Beziehungen zwischen der Arithmetik und dem Code, so dass es keinen Sinn macht, das Natürliche in der Arithmetik zu verbieten, es sei denn, es gibt einen guten Grund. (Die andere Sache, die Sprachen im C-Stil aus der Verwendung des Gleichheits-Symbols übernommen haben, ist die Verwendung von == für den Gleichheitsvergleich. Hier jedoch wäre diese Art der Verkettung unmöglich, da == ganz rechts einen Wert zurückgibt.)
quelle
a = b = c
bedeutet Lernen , dassa
undb
undc
dasselbe sind. Wenn eine C - Stil Sprache zu lernen lernen wir , dass nacha = b = c
,a
undb
dasselbe sind wiec
. Es gibt sicherlich einen Unterschied in der Semantik, wie meine Antwort selbst sagt, aber als ich als Kind zum ersten Mal lernte, in einer Sprache zu programmieren, die zwar=
für Aufgaben verwendet wurde, dies aber nicht zuließ,a = b = c
schien mir dies jedoch unvernünftig…=
für den Gleichheitsvergleich verwendet wird. Dasa = b = c
müsste also bedeuten, wasa = b == c
in Sprachen im C-Stil bedeutet. Ich fand die in C erlaubte Verkettung viel intuitiver, weil ich eine Analogie zur Arithmetik ziehen konnte.Ich verwende gerne den Rückgabewert für die Zuweisung, wenn ich eine Reihe von Informationen aktualisieren und zurückgeben muss, ob Änderungen vorgenommen wurden oder nicht:
Sei aber vorsichtig. Sie könnten denken, Sie können es auf diese verkürzen:
Dies stoppt jedoch tatsächlich die Auswertung der oder -Anweisungen, nachdem die erste Wahrheit gefunden wurde. In diesem Fall bedeutet dies, dass die Zuweisung nachfolgender Werte beendet wird, sobald der erste abweichende Wert zugewiesen wird.
Siehe https://dotnetfiddle.net/e05Rh8 , um damit herumzuspielen
quelle