Во-первых, ответы Хенка и Оливье верны; Я хочу объяснить это немного иначе. В частности, я хочу коснуться вашего замечания. У вас есть такой набор утверждений:
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;
Убедитесь, что это ясно, потому что вы ошиблись в этом шаге . Разбивая сложные операции на более простые, вы должны делать это медленно и осторожно и не пропускать шаги . Пропуск шагов - вот где мы делаем ошибки.
Хорошо, теперь снова разделите присвоение на t2, медленно и осторожно.
int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;
Присваивание присвоит t2 то же значение, что и c, поэтому предположим, что:
int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;
Отлично. Теперь разбейте вторую строку:
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;
Теперь разбейте третью строку:
int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
А теперь мы можем посмотреть на все:
int k = 10;
int c = 30;
int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Итак, когда мы закончили, k будет 80, а c равно 70.
Теперь посмотрим, как это реализовано в IL:
int t1 = k;
int t3 = c;
is implemented as
ldloc.0
ldloc.1
Теперь это немного сложно:
int t4 = k + c;
k = t4;
is implemented as
ldloc.0
ldloc.1
add
dup
stloc.0
Мы могли бы реализовать это как
ldloc.0
ldloc.1
add
stloc.0
ldloc.0
но мы используем трюк "dup", потому что он делает код короче и облегчает джиттер, и мы получаем тот же результат. Как правило, генератор кода C # старается сохранять временные файлы в стеке как можно более "эфемерными". Если вам легче следовать IL с меньшим количеством эфемеров, включите оптимизации от , и генератор кода будет менее агрессивным.
Теперь нам нужно проделать тот же трюк, чтобы получить c:
int t2 = t3 + t4;
c = t2;
is implemented as:
add
dup
stloc.1
и наконец:
k = t1 + t2;
is implemented as
add
stloc.0
Поскольку эта сумма нам ни на что не нужна, мы ее не дублируем. Стек теперь пуст, и мы подошли к концу оператора.
Мораль этой истории такова: когда вы пытаетесь понять сложную программу, всегда разбивайте операции по одной . Не выбирайте короткие пути; они сбивают вас с пути.