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
beforeFoo
Foo
beforeBar
Bop
beforeBar
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?
CodePudding user response:
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, ifM
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.