0
 class Program
 {
  static void Main(string[] args)
  {
   bool success = true;
   int[] array = { 10, 15, 20 };
   foreach (var i in array)
    success = success && SynchronizeAccount(i);
  }

  static bool SynchronizeAccount(int i)
  {
   Console.WriteLine(i);
   return false;
  }
 }

Output is 10. After first step 'success' becomes false and never will be true, so c# stops loop execution after first iteration. But I need SIDE effect of SynchronizeAccount, not 'success' value.

Niels Keurentjes
  • 41,402
  • 9
  • 98
  • 136
Denis
  • 3,653
  • 4
  • 30
  • 43
  • @GrantWinney no it is definitely a bug in the compiler ;) – MichaC Dec 21 '13 at 13:39
  • 1
    So you really think you found a bug in the compiler, huh? Winning one million dollars in a lottery without even having purchased a fortune is more likely. – Thomas Weller Dec 21 '13 at 13:41
  • When I saw the first title of the post I was literally blown away but when going through the post, I was like nahhh! Now the title is quite appropriate! – Neo182 Dec 21 '13 at 13:42

5 Answers5

19

This is not a bug, as in nearly all programming languages C# evaluates && lazily - if the left operand is already false, the whole expression can never become true, so it's not required anymore to evaluate the right operand of the expression.

Flip the operands or change to success & SynchronizeAccount to force evaluation of both operands.

Note that it's a unique feature of C# that you can apply both & and && to boolean values - in most other languages including Java and PHP a single ampersand (&) is the bitwise AND, which often provides completely different results.

Niels Keurentjes
  • 41,402
  • 9
  • 98
  • 136
  • Correct explanation of the difference between `success && SynchronizeAccount` and `success & SynchronizeAccount`. Note that it is also possible to use compound assignment in the original question, so `success &= SynchronizeAccount;`. – Jeppe Stig Nielsen Dec 21 '13 at 13:44
8

This is not a bug, this is normal behavior. The && operator will only evaluate the right side of the operator if the left side evaluate to true:

The conditional-AND operator (&&) performs a logical-AND of its bool operands, but only evaluates its second operand if necessary.

So after the first iteration, success evaluates to false, and SynchronizeAccount does not get called again.

If you want to evaluate SynchronizeAccount regardless of what it returns, use & operator instead:

foreach (var i in array)
    success = success & SynchronizeAccount(i);

Or more simply:

foreach (var i in array)
    success &= SynchronizeAccount(i);

Or perhaps use a little Linq:

bool success = array.Aggregate(true, (b, i) => b & SynchronizeAccount(i));
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
4

This is not a bug, It is called Short-circuit evaluation. If you really need to get the second method to work too, us & instead of &&

Mario Stoilov
  • 3,411
  • 5
  • 31
  • 51
1

I wouldn't treat this as a bug; it looks like simply an optimization. "&&" is short-circuit so compiler is permitted to optimize the method calling if its result isn't needed.

You could try rewriting this like


foreach (var i in array) {
  if(!SynchronizeAccount())
    success = false;
}
Netch
  • 4,171
  • 1
  • 19
  • 31
  • 1
    Not only permitted to optimize, it is required to. Like if I say `if (x != null && x.IsNew) { ... }`, it is required that the right-hand side of the `&&` operator is ***not*** evaluated if the left-hand side is `false` (evaluating the right-hand side in that case would lead to an exception). – Jeppe Stig Nielsen Dec 21 '13 at 13:51
  • @JeppeStigNielsen you are quite right; but I've responded to the words "stops loop execution". It could be simply topic starter opinion but, if setting "success" is the only action at the loop body, a compiler could optimize it with stopping the whole loop. This doesn't change any visible result except, probably, the spent time. – Netch Dec 21 '13 at 15:47
1

Consider removing success from your loop.

The other answers are awesome for explaining & vs &&, but I'm not sure what purpose success even has in your code. Since you intend to ignore it and iterate through all the items in array anyway, just call SynchronizeAccount inside your foreach loop and ignore the returned value.

static void Main(string[] args)
{
    int[] array = { 10, 15, 20 };
    foreach (var i in array)
        SynchronizeAccount(i);
}
Grant Winney
  • 65,241
  • 13
  • 115
  • 165