Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does C# guarantee evaluation order of branched nested expressions?

C# handles both nested and chained expressions, obviously. If the nesting and/or chaining is linear then it's evident what order the expressions are evaluated in:

Foo(Bar(Baz().Bop())) can only evaluate in the following order:

  • Baz()
  • Bop()
  • Bar()
  • Foo()

But what if the nesting isn't linear? Consider: Foo(Baz()).Bar(Bop())

Clearly the following MUST all be true:

  • Baz before Foo
  • Foo before Bar
  • Bop before Bar

But it's not clear exactly when Bop will be evaluated. Any of the following would be a viable order:

  • Possibility #1
    • Bop()
    • Baz()
    • Foo()
    • Bar()
  • Possibility #2
    • Baz()
    • Bop()
    • Foo()
    • Bar()
  • Possibility #3
    • Baz()
    • Foo()
    • Bop()
    • Bar()

My instinct is that the 3rd option is likely correct. i.e. that it will fully evaluate Foo(Baz()) before it starts to evaluate any of .Bar(Bop())

Whilst I could certainly test an individual situation to see what happens, that doesn't tell me whether my guess will always be true?

But my question is: Is the order of evaluation of branched nested expressions defined as part of the C# language specification, or left to the situational judgement of the compiler?

If not, is it at least known to be deterministic?

like image 829
Brondahl Avatar asked Sep 05 '25 03:09

Brondahl


1 Answers

You'll find the answers in Section 11 of the specification.

Specifically, 11.6.6 Function member invocation says:

The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:
...

  • E is evaluated. If this evaluation causes an exception, then no further steps are executed.
  • The argument list is evaluated as described in §11.6.2.

So, given an expression E.M(A), E is fully evaluated before A is evaluated.

For the Foo(Baz()).Bar(Bop()) case, if we're looking at the evaluation of Bar (so E is Foo(Baz()), M is Bar and the argument list is Bop()), this means that Foo (E) must have been fully evaluated before Bop (the argument list) is evaluated, meaning that "possibility #3" is the correct one.

There's also 11.6.2.3 Run-time evaluation of argument lists:

During the run-time processing of a function member invocation (§11.6.6), the expressions or variable references of an argument list are evaluated in order, from left to right

So in the expression M(A, B), A is fully evaluated before B is evaluated.

like image 129
canton7 Avatar answered Sep 07 '25 20:09

canton7



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!