首先,Henk 和 Olivier 的答案是正确的;我想用稍微不同的方式来解释它。具体来说,我想谈谈你提出的这一点。你有这组语句:
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; // 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.
由于我们不需要其他任何东西的总和,因此我们不会重复它。堆栈现在是空的,我们在语句的末尾。
这个故事的寓意是:当你试图理解一个复杂的程序时,总是一次分解一个操作。不要走捷径;他们会让你误入歧途。