I ran into a nasty bug recently and the simplified code looks like below:
int x = 0;
x += Increment(ref x);
...
private int Increment(ref int parameter) {
parameter += 1;
return 1;
}
The value of x after the Increment call is 1! This was an easy fix once I found out what was happening. I assigned the return value to a temporary variable and then updated x. I was wondering what explains this issue. Is it something in the spec or some aspect of C# that I'm overlooking.
+= reads the left argument then the right one, so it reads the variable, executes the method that increments, sums the results, and assigns to the variable. In this case, it reads 0, computes 1 with a side effect of changing the variable to 1, sums to 1, and assigns 1 for the variable. The IL confirms this, as it shows loads, a call, an add, and a store in that order.
Altering the return to 2 to see the result is 2 confirms that the method's return value is the part that "sticks."
Since someone asked, here's the full IL via LINQPad with its annotations:
IL_0000: ldc.i4.0
IL_0001: stloc.0 // x
IL_0002: ldloc.0 // x
IL_0003: ldloca.s 00 // x
IL_0005: call UserQuery.Increment
IL_000A: add
IL_000B: stloc.0 // x
IL_000C: ldloc.0 // x
IL_000D: call LINQPad.Extensions.Dump
Increment:
IL_0000: ldarg.0
IL_0001: dup
IL_0002: ldind.i4
IL_0003: ldc.i4.1
IL_0004: add
IL_0005: stind.i4
IL_0006: ldc.i4.2
IL_0007: ret
Note that on line IL_000A, the stack contains the load of x (which was 0 when it was loaded) and the return value of Increment (which is 2). It then runs add
and stloc.0
without further inspection of x's value.