Short circuit on |= and &= assignment operators in C#

一曲冷凌霜 提交于 2019-11-27 04:59:44

The C# specification guarantees that both sides are evaluated exactly once from left-to-right and that no short-circuiting occurs.

5.3.3.21 General rules for expressions with embedded expressions

The following rules apply to these kinds of expressions: parenthesized expressions (§7.6.3), element access expressions (§7.6.6), base access expressions with indexing (§7.6.8), increment and decrement expressions (§7.6.9, §7.7.5), cast expressions (§7.7.6), unary +, -, ~, * expressions, binary +, -, *, /, %, <<, >>, <, <=, >, >=, ==, !=, is, as, &, |, ^ expressions (§7.8, §7.9, §7.10, §7.11), compound assignment expressions (§7.17.2), checked and unchecked expressions (§7.6.12), plus array and delegate creation expressions (§7.6.10).

Each of these expressions has one or more sub-expressions that are unconditionally evaluated in a fixed order.

The C# specification for compound operators says:

7.17.2 Compound assignment

...

An operation of the form x op= y is processed by applying binary operator overload resolution (§7.3.4) as if the operation was written x op y. Then,

  • If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.

  • Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of x, and if y is implicitly convertible to the type of x or the operator is a shift operator, then the operation is evaluated as x = (T)(x op y), where T is the type of x, except that x is evaluated only once.

...

In your case op is & or |. The short circuiting behavior mirrors that of &/| and not &&/||.


Note that this only refers to behavior visible in a single threaded scenario. So if the right hand side has no side-effects that are observable in such a scenario, the compiler or JITter is still free to omit the evaluation.

In your example the compiler is free to terminate the loop once it knows the result, since there are no such side-effects. But it's not required to do so.

In particular timing does not count as such a side-effect, and you thus can't rely on your code having constant runtime. This can be problematic in a security context, since it can introduce a timing side-channel.

but do |= and &= short-circuit too?

No. &= and |= are the equivalents of the operations & and |, not of the short-circuited logical operators.

No, the &= and |= operators are not doing short-circuit evaluation.

They are pseudo-operators, which the compiler converts into use of the & and | operators.

This code:

allEven &= IsEven(numbers[i]);

is exactly equivalent to:

allEven = allEven & IsEven(numbers[i]);

If you want the short circuited check, then you have to write it out using the short circuiting versions of the operators:

allEven = allEven && IsEven(numbers[i]);

There is no &&= pseudo operator, but the code above is exactly what the compiler would do if there was one.

Easy to find out:

  bool b = false;
  b &= Foo(1);


    private static bool Foo(int id)
    {
        Console.WriteLine("test " + id);
        return false;
    }

The answer is No, Foo() is always executed.

But if you're looking for an optimization, the real issue is of course in the loop:

 allEven = numbers.All(n => IsEven(n));

could be a lot faster. It will stop iterating after seeing the first odd number (9 in the sample).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!