Em primeiro lugar, as respostas de Henk e Olivier estão corretas; Quero explicar de uma maneira ligeiramente diferente. Especificamente, quero abordar esse ponto que você fez. Você tem este conjunto de afirmações:
int k = 10;
int c = 30;
k += c += k += c;
E então você conclui incorretamente que isso deve dar o mesmo resultado que este conjunto de afirmações:
int k = 10;
int c = 30;
k += c;
c += k;
k += c;
É informativo saber como você errou e como fazer da maneira certa. A maneira certa de decompô-lo é assim.
Primeiro, reescreva o mais externo + =
k = k + (c += k += c);
Em segundo lugar, reescrever o + externo. Espero que você concorde que x = y + z deve ser sempre o mesmo que "avalie y para um temporário, avalie z para um temporário, some os temporários, atribua a soma a x" . Então, vamos deixar isso bem explícito:
int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;
Certifique-se de que está claro, porque essa é a etapa que você errou . Ao dividir operações complexas em operações mais simples, você deve certificar-se de fazê-lo lenta e cuidadosamente e não pular etapas . Pular etapas é onde cometemos erros.
OK, agora divida a atribuição para t2, de novo, lenta e cuidadosamente.
int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;
A atribuição atribuirá a t2 o mesmo valor atribuído a c, então digamos que:
int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;
Ótimo. Agora divida a segunda linha:
int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Ótimo, estamos progredindo. Divida a atribuição para t4:
int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Agora divida a terceira linha:
int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
E agora podemos olhar para tudo:
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
Então, quando terminarmos, k é 80 e c é 70.
Agora, vamos ver como isso é implementado no IL:
int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3
Agora, isso é um pouco complicado:
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
Poderíamos ter implementado o acima como
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
mas usamos o truque "dup" porque torna o código mais curto e mais fácil no jitter, e obtemos o mesmo resultado. Em geral, o gerador de código C # tenta manter os temporários "efêmeros" na pilha tanto quanto possível. Se você achar que é mais fácil de seguir do IL com menos Ephemerals, vire otimizações off , e o gerador de código será menos agressiva.
Agora temos que fazer o mesmo truque para obter 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.
e finalmente:
k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.
Como não precisamos da soma para mais nada, não a enganamos. A pilha agora está vazia e estamos no final da instrução.
A moral da história é: quando você está tentando entender um programa complicado, sempre divida as operações uma de cada vez . Não tome atalhos; eles o desviarão.