I always thought that A ^= B ^= A ^= B swaps A <-> B. My guess that this line should be evaluated right to left in 3 steps:
1) A ^= B;
2) B ^= A;
3) A ^= B;
But somehow in C# A becomes 0 if you do it in one line. I've looked into the assembly and found that original A is being stored first and somewhy in the last third step instead of taking the actual current value of A, the code uses cached original value. Assembly looks like this:
mov         eax,dword ptr [ebp-40h]  //eax <- A
mov         dword ptr [ebp-7Ch],eax  //C <- A (why cache?)
mov         eax,dword ptr [ebp-44h]  //eax <- B
xor         dword ptr [ebp-40h],eax  //A ^= B
mov         eax,dword ptr [ebp-40h]  //eax <- A
xor         dword ptr [ebp-44h],eax  //B ^= A
mov         eax,dword ptr [ebp-7Ch]  //eax <- C (?)
xor         eax,dword ptr [ebp-44h]  //eax ^= B (= C ^ B)
mov         dword ptr [ebp-40h],eax  //A = C ^ B (instead of A ^ B)
It seems ok in C++ and assembly is using only 2 variables:
mov         eax,dword ptr [a]
xor         eax,dword ptr [b]
mov         dword ptr [a],eax
mov         ecx,dword ptr [b]
xor         ecx,dword ptr [a]
mov         dword ptr [b],ecx
mov         edx,dword ptr [a]
xor         edx,dword ptr [b]
mov         dword ptr [a],edx
Am I missing something?