Zunächst einmal sind die Antworten von Henk und Olivier richtig; Ich möchte es etwas anders erklären. Insbesondere möchte ich auf diesen Punkt eingehen, den Sie angesprochen haben. Sie haben folgende Aussagen:
int k = 10;
int c = 30;
k += c += k += c;
Und Sie schließen dann fälschlicherweise, dass dies das gleiche Ergebnis wie diese Aussagen liefern sollte:
int k = 10;
int c = 30;
k += c;
c += k;
k += c;
Es ist informativ zu sehen, wie Sie das falsch verstanden haben und wie Sie es richtig machen. Der richtige Weg, es zu brechen, ist so.
Schreiben Sie zuerst das äußerste + = um
k = k + (c += k += c);
Zweitens schreiben Sie das äußerste + neu. Ich hoffe, Sie stimmen zu, dass x = y + z immer dasselbe sein muss wie "y als temporär bewerten, z als temporär bewerten, temporäre Summe summieren, x die Summe zuweisen" . Lassen Sie uns das sehr deutlich machen:
int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;
Stellen Sie sicher, dass dies klar ist, da dies der Schritt ist, den Sie falsch verstanden haben . Wenn Sie komplexe Vorgänge in einfachere Vorgänge aufteilen, müssen Sie sicherstellen, dass Sie dies langsam und vorsichtig tun und keine Schritte überspringen . Beim Überspringen von Schritten machen wir Fehler.
OK, jetzt brechen Sie die Zuordnung zu t2 wieder langsam und vorsichtig auf.
int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;
Die Zuweisung weist t2 den gleichen Wert zu, der c zugewiesen ist. Nehmen wir also Folgendes an:
int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;
Toll. Brechen Sie nun die zweite Zeile auf:
int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Großartig, wir machen Fortschritte. Teilen Sie die Zuordnung zu t4 auf:
int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Brechen Sie nun die dritte Zeile auf:
int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Und jetzt können wir uns das Ganze ansehen:
int k = 10; // 10
int c = 30; // 30
int t1 = k; // 10
int t3 = c; // 30
int t4 = k + c; // 40
k = t4; // 40
int t2 = t3 + t4; // 70
c = t2; // 70
k = t1 + t2; // 80
Wenn wir fertig sind, ist k 80 und c ist 70.
Schauen wir uns nun an, wie dies in der IL implementiert ist:
int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3
Das ist ein bisschen schwierig:
int t4 = k + c;
k = t4;
is implemented as
ldloc.0 // load k
ldloc.1 // load c
add // sum them to stack slot 3
dup // t4 is stack slot 3, and is now equal to the sum
stloc.0 // k is now also equal to the sum
Wir hätten das oben Gesagte als umsetzen können
ldloc.0 // load k
ldloc.1 // load c
add // sum them
stloc.0 // k is now equal to the sum
ldloc.0 // t4 is now equal to k
Aber wir verwenden den "dup" -Trick, weil er den Code kürzer macht und den Jitter erleichtert, und wir erhalten das gleiche Ergebnis. Im Allgemeinen versucht der C # -Codegenerator, temporäre Elemente so kurz wie möglich auf dem Stapel zu halten. Wenn Sie es einfacher finden , die IL mit weniger Ephemerals zu folgen, drehen Optimierungen aus , und der Code - Generator wird weniger aggressiv.
Wir müssen jetzt den gleichen Trick machen, um c zu erhalten:
int t2 = t3 + t4; // 70
c = t2; // 70
is implemented as:
add // t3 and t4 are the top of the stack.
dup
stloc.1 // again, we do the dup trick to get the sum in
// both c and t2, which is stack slot 2.
und schlussendlich:
k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.
Da wir die Summe für nichts anderes brauchen, täuschen wir sie nicht. Der Stapel ist jetzt leer und wir sind am Ende der Anweisung.
Die Moral der Geschichte lautet: Wenn Sie versuchen, ein kompliziertes Programm zu verstehen, brechen Sie die Operationen immer einzeln auf . Nehmen Sie keine Abkürzungen; Sie werden dich in die Irre führen.