まず、ヘンクとオリビエの答えは正しいです。少し違う方法で説明したいと思います。具体的には、この点についてお話ししたいと思います。次のステートメントセットがあります。
int k = 10;
int c = 30;
k += c += k += c;
そして、次のステートメントのセットと同じ結果が得られるはずであると誤って結論付けます。
int k = 10;
int c = 30;
k += c;
c += k;
k += c;
どのようにしてそれを間違ったのか、そしてそれを正しく行う方法を知ることは有益です。それを分解する正しい方法はこのようなものです。
まず、最も外側の+ =を書き換えます
k = k + (c += k += c);
次に、最も外側の+を書き換えます。 x = y + zは常に「yを一時的に評価し、zを一時的に評価し、一時を合計し、合計をxに割り当てる」と同じでなければならないことに同意してください。だからそれを非常に明示的にしましょう:
int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;
これがあなたが間違ったステップなので、それが明確であることを確認してください。複雑な操作をより単純な操作に分解する場合は、ゆっくりと慎重に行う必要があり、手順をスキップしないでください。手順をスキップすると、間違いが発生します。
OK、今度はt2への割り当てを、ゆっくりと慎重に分解します。
int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;
割り当ては、cに割り当てられているのと同じ値をt2に割り当てるので、次のようにしましょう。
int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;
すごい。次に、2行目を分解します。
int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
すばらしい、私たちは進歩しています。t4への割り当てを分解します。
int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
次に3行目を分解します。
int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
そして今、全体を見ることができます:
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
完了したら、kは80、cは70です。
次に、これがILにどのように実装されているかを見てみましょう。
int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3
これは少しトリッキーです:
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
上記を次のように実装することもできます
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
"dup"トリックを使用しているのは、コードが短くなり、ジッターが軽減され、同じ結果が得られるためです。 一般に、C#コードジェネレーターは、一時的なものをできるだけ「スタック上」に保持しようとします。エフェメラルが少ないILを追跡する方が簡単である場合は、最適化をオフにすると、コードジェネレーターの攻撃性が低下します。
cを取得するために、同じトリックを実行する必要があります。
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.
そして最後に:
k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.
それ以外には合計が必要ないため、重複はしません。これでスタックは空になり、ステートメントの終わりです。
この話の教訓は、複雑なプログラムを理解しようとするときは、常に1つずつ操作を分解することです。近道をしないでください。彼らはあなたを迷わせます。