C#中的前置和后置增量

76

我有点困惑C#编译器如何处理前缀和后缀的增减运算符。

当我编写以下代码时:

int x = 4;
x = x++ + ++x;

x之后将具有值10。我认为这是因为预增量将x设置为5,使其成为5 + 5,评估为10。然后,后增量将更新x6,但不会使用此值,因为然后10将被赋给x

但当我编码:

int x = 4;
x = x-- - --x;

之后x将为2。 有人能解释一下这是为什么吗?


4
好问题!我们学到了永远不要写需要思考它可能做什么的代码,而是要考虑它将会做什么。 - RvdK
1
这就是为什么我通常完全避免使用++和--的原因。@Giorgio,我经常想知道它们为什么被引入到C#中。 - Andy
6个回答

60

x-- 的值会变为 4,在--x的时候,它的值是 3,所以最后的结果是 2,然后你就会得到:

x = 4 - 2

顺便说一下,你的第一个案例将是x = 4 + 6

以下是一个小例子,它将打印出每个部分的值,也许这样你会更好地理解:

static void Main(string[] args)
{
    int x = 4;
    Console.WriteLine("x++: {0}", x++); //after this statement x = 5
    Console.WriteLine("++x: {0}", ++x); 

    int y = 4;
    Console.WriteLine("y--: {0}", y--); //after this statement y = 3
    Console.WriteLine("--y: {0}", --y);

    Console.ReadKey();
}
这将打印出来。
x++: 4
++x: 6
y--: 4
--y: 2

6
谢谢您的回答 - 我之前认为后增量和前增量会在代码行完全评估之后/之前执行 - 但它们实际上是在每个表达式项被评估之后/之前执行。 - Schweder
1
@Schweder,稍作更正:运算符在对其应用的变量进行评估之后/之前执行,而不是表达式中的每个术语。 - user47589
6
@Inuyasha:对你的更正进行修正:运算符总是在将变量作为变量进行评估之后执行。前缀和后缀运算符之间的区别只在于返回的值,而不是操作执行的顺序。 - Eric Lippert

19

让我们看一下从该语句生成的IL代码

IL_0002:  ldloc.0     

将x的值加载到堆栈中。 堆栈=>(4)

IL_0003:  dup         

复制栈顶的元素。栈 => (4, 4)

IL_0004:  ldc.i4.1    

将数字1推入栈中。栈 => (1, 4, 4)

IL_0005:  sub         

将栈顶两个值相减并将结果推入栈中。栈 => (3, 4)

IL_0006:  stloc.0     

将栈顶的值存回 x。栈 => (4)

IL_0007:  ldloc.0     

将x的值重新加载到堆栈中。 堆栈=>(3,4)

IL_0008:  ldc.i4.1    

将数值1加载到堆栈上。堆栈 => (1, 3, 4)

IL_0009:  sub         

将两者相减。Stack => (2, 4)

IL_000A:  dup         

复制顶部值 => (2, 2, 4)

IL_000B:  stloc.0     

将顶部的值存回x。栈 => (2, 4)

IL_000C:  sub      

将两个顶部的值相减。堆栈 => (2)

IL_000D:  stloc.0  

将此值存回到 x 中。 x == 2


9

根据您的评论:

我认为后置和前置自增在完整代码行的评估之后/之前执行,但它们在表达式中每个项目的评估之后/之前执行。

您的误解是非常普遍的。请注意,在某些语言(如C)中,副作用何时变得可见并没有指定,因此在C中您的语句合法但不是必须的。

但在C#中情况并非如此;在C#中,左侧代码的副作用总是在右侧代码执行之前发生(在单线程中;在多线程场景中则无法保证)。

有关C#中增量运算符的详细说明,请参见:

i++和++i之间有什么区别?

那里还有许多其他链接,这些链接指向我撰写的有关这个经常被误解的主题的文章。


6
您将会发现,使用C++.Net编译器的答案完全不同。
int x = 4;
x = x++ + ++x; // x = 11
x = 4;
x = x-- - --x; // x = -1

当然,结果的差异取决于不同的语义 - 这似乎很正常。但是,尽管理解了这一点,两个 .net 编译器在这样基本的事情上表现不一致的事实仍然让我感到困惑。

2
在C++中(不是Microsoft的非C++版本),在完整表达式结束之前多次修改变量会产生未定义行为。 - Sebastian Mach
4
这与语法分析毫不相关,而是与两种语言在序列点方面允许的语义差异有关。 - Eric Lippert

3
在这个例子中,
int x = 4;
x = x++ + ++x;

你可以像这样进行拆分:

x = 4++; which is = 5
x = 4 + ++5; which is 4 + 6
x = 10

同样地,

int x = 4;
x = x-- - --x;

在这里,

x = 4--; which is = 3
x = 4 - --3; which is 4 - 2
x = 2

简单来说,你可以替换x的当前值,但是对于每个++或--,都要从x中添加/减去一个值。


-1

我认为 ++ + ++ 的解释是错误的:

命令...........X的值

..................未定义

int x=4 ..........4

x++...............5(第一个加数为4)

++x...............6(第二个加数为6)

x=summand1+summand2 ..4+6=10

类似地,-- - -- 的解释为:

命令...........X的值

..................未定义

int x=4 ..........4

x--...............3(减数为4)

--x...............2(被减数为2)

x=subtractor-subtrahend ..4-2=10


我完全不理解你的回答,抱歉。 - Sebastian Mach
@phresnel,我在答案中纠正了几个错误。他基本上说的和你接受的答案一样:我们实际上得到10是因为我们加了4和6;2是4和2之间的差异。 - phoog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接