C#多重赋值中中间值的损失

C#多重赋值中中间值的损失,c#,C#,给定… int a = 1, b = 4; a += b += a += b; (a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4])) (a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4])) // ^ re-use of original valu

给定…

int a = 1, b = 4;
a += b += a += b;  
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
(a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
                        //        ^ re-use of original value of a (1)  
                        //          instead of expected intermediate right-to-left  
                        //          evaluation result (5)  
然后…

int a = 1, b = 4;
a += b += a += b;  
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
(a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
                        //        ^ re-use of original value of a (1)  
                        //          instead of expected intermediate right-to-left  
                        //          evaluation result (5)  

<强>在C++ + < /强>

中进行了评估。
int a = 1, b = 4;
a += b += a += b;  
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
(a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
                        //        ^ re-use of original value of a (1)  
                        //          instead of expected intermediate right-to-left  
                        //          evaluation result (5)  
。。。在C#中……

int a = 1, b = 4;
a += b += a += b;  
(a += (b += (a += b))); // a = 14[5 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
(a += (b += (a += b))); // a = 10[1 + 9] (b = 9[4 + 5] (a = 5[1 + 4]))  
                        //        ^ re-use of original value of a (1)  
                        //          instead of expected intermediate right-to-left  
                        //          evaluation result (5)  
以上内容已经在VisualStudio2008和2012中进行了测试,因此不存在最近引入的语言错误的怀疑

然而,我确实希望C#的行为能模仿C++,并假设我需要教育。我理解表达式求值逻辑的方式,不需要解释MSIL。在进行了相当广泛的搜索之后,在语言规范中没有找到相关的细节,我希望语言专家能够解释为什么会这样

对于那些想知道我到底为什么要这么做的好奇的人。。。有一个简单的C++技巧,用于两个整数类型的一个非常高效和整洁的内联交换,这是这样的……BR>

我很失望地发现它在C#中不起作用,也很好奇为什么。这是关于理解C#的低级机制及其背后的原理。请不要多,不要少,特别是不要易读

NB 请各位,这不是一个认真的努力,在挤压到一行上的一切
对于C(以及更高版本的C++),操作符的优先级和结合性总是被精确定义的。 赋值运算符的标准从右向左关联性使C/C++版本的行为100%清晰且可预测。它对我在几个编译器和平台上起作用,我会考虑一个没有这样的编译器出错。 我承认这行代码是模糊的,这是一种好奇心,我可能会给初级C/C++程序员一个“脑筋急转弯”。 显然,C#是不同的——而且显然是有意的。
我试图解释导致C#的行为与它的一种祖先语言不同的设计考虑。即使是受过教育的猜测也会受到欢迎

回答 感谢亚历山大·斯捷潘尼乌克。
从操作数的左到右求值开始

a        = 1 + theRest1     // (1)  
theRest1 = b = 4 + theRest2 // (2)  
theRest2 = a = 1 + 4        // (3)  
(3) into (2): theRest1 = b = 4 + 5  
(2) into (1): a = 1 + 9  
“为什么”的解释仍将不胜感激。
但C#规范清楚地表明,上述评估是正确的。
下面是我们可以使用的2个变量,即C++技巧……
b ^= a ^= b;
a ^ = b;

我不喜欢它,因为它打破了我的直觉;-)

它也不能保证在C/C++中工作。在单个语句中多次赋值的变量值未定义

它能否工作取决于实现——您可能会发现编译器或体系结构根本不起作用

C#只是对它要求严格,这不是坏事


无论如何,xor技巧在现代体系结构上是毫无意义的。与仅使用临时变量相比,底层实现的效率更低。现代CPU上有2个以上的寄存器。

甚至可以追溯到c

K&R有这样一个例子:

a[i++] = i;
这是未定义的行为,在一行中修改和使用变量。一个编译器可以以一种方式工作,也可以以另一种方式工作。标准的定义是不明确的,这样你就不会依赖它了

K&R建议使用单独的行或中间变量。不是为了人类的可读性,而是为了使编译器的解释明确无误

a ^= b ^= a ^= b;
因此,这就成了一个明确的问题:

a ^= b; b ^= a; a ^= b; // can still on one line if that is important to you!

有关更多未定义的行为,请参见此处:

您实际上已经自己解释了所有内容:)

“a(1)原值的重复使用”

为什么会这样? 你可以找到很好的解释

简而言之
a+=anything
等同于
a=a+anything

所以编译器需要计算表达式
a+任何内容的两个操作数,然后将结果分配给
a

1.计算第一个操作数(即
a
)。评估结果为1
2.计算第二个操作数(即
表达式
)。评价结果为9。评估的副作用是
a
包含5个
3.计算第一个操作数和第二个操作数之和。结果是10
4.结果被分配给
a
。现在
a
包含10个

希望有帮助

更新
根据:表达式中操作数的求值顺序为从左到右。参见第150页的§14.2。

因此,这种行为在C#中是指定和正确的。

如果您希望获得这些类型的性能提升,那么您使用C#到底是为了什么??我很欣赏你的好奇心,但无论如何,这对任何现代建筑来说都不是一个性能提升。它可能是慢的,并且一个错误等待发生。在C++中,优先级和关联性可能被重复,评价顺序不是。C/C++中赋值操作符的右向左结合性定义良好,并且在不同优先级(+,[])的操作符中,我的示例不混合。.你可能教了我一些东西。我一直都明白赋值链式(在C++中)的工作原理是准确的(而且只是),因为赋值语句总是返回其赋值的规范。我不知道有一条规则禁止在一条语句中对单个变量进行多个赋值。你有这条新闻的来源/链接吗?@AlanK“sequence points”我的问题不是发生了什么,而是为什么?为什么C#从右向左工作,所以。。。为a分配一个新值,然后使用a的新值为b分配一个新值,然后将a的旧值与b的新值相加以进行最终分配?对于赋值,C#运算符优先级和关联性规则与C/C++相同。那么为什么会有行为上的差异呢?我已经参考规范更新了我的答案。C#从左到右计算二进制表达式,这就是为什么使用了
a
的旧值。谢谢。这就是我需要的。我已经根据这个新的理解更新了我原来的问题