The following code produces a syntax error:
class Foo
{
public Action a = () => { };
}
void doSomething()
{
var foo = new Foo();
(foo.a)(); // error CS1525: Invalid expression term ')'
}
However, the following alternatives both work:
foo.a(); // works
Action a = foo.a; a(); // works
Why is this so? (foo.a) is an Action; why can't I call it?
That is because the piece of code (foo.a)(); would evaluate to a cast-expression, as foo.a could be either a member-access or a type.
And that's why the compiler is searching for an expression inside the empty brackets.
For example consider the following snippet:
Func<string, int> fn = null;
int x = (fn)("asd"); // The type or namespace "fn" could not be found
Here the compiler clearly states that it interpreted it as a cast-expression, as it expects fn to be a type.
So, inside the first set of brackets nothing can be that could be read as a type, because that will cause the whole expression to be read as a cast-expression.
The following snippet compiles:
(new Action<int>(x => { Console.WriteLine(x); }))(1);
(new Action(() => { Console.WriteLine("asd1"); }))();
((Console.WriteLine))("asd2");
((Action)null)();
Action a = null;
(true ? a : null)();
((a))();
But these lines do not:
(Console.WriteLine)("asd");
Action a = null;
(a)();
Action<string> b = null;
(b)("");
It turns out that the syntactic grammar is ambiguous. Why does the parser interpret (a)() as a cast-expression, when it is not, but it could interpret it as an invocation-expression? When does the parser decide if it is going to be a cast or an invocation? Well, they thought of this. The specification says (§7.7.6):
The grammar for a cast-expression leads to certain syntactic ambiguities. For example, the expression (x)–y could either be interpreted as a cast-expression (a cast of –y to type x) or as an additive-expression combined with a parenthesized-expression (which computes the value x – y).
To resolve cast-expression ambiguities, the following rule exists: A sequence of one or more tokens (§2.3.3) enclosed in parentheses is considered the start of a cast-expression only if at least one of the following are true:
- The sequence of tokens is correct grammar for a type, but not for an expression.
- The sequence of tokens is correct grammar for a type, and the token immediately following the closing parentheses is the token “~”, the token “!”, the token “(”, an identifier (§2.4.1), a literal (§2.4.4), or any keyword (§2.4.3) except as and is.
In this case, "The sequence of tokens is correct grammar for a type, and the token immediately following the closing parentheses is the token (" stands, so this is the end of the story.
From the C# 5.0 Language Specification
cast-expression:
( type ) unary-expression
unary-expression:
primary-expression
...
cast-expression
await-expression
expression:
non-assignment-expression
assignment
non-assignment-expression:
conditional-expression
lambda-expression
query-expression
// conditional expression at the end can contain a single unary-expression
invocation-expression:
primary-expression ( argument-list_opt )
primary-expression:
primary-no-array-creation-expression
array-creation-expression
primary-no-array-creation-expression:
literal
simple-name
parenthesized-expression
...
parenthesized-expression:
( expression )
expression-statement:
statement-expression ;
statement-expression:
invocation-expression
object-creation-expression
assignment
post-increment-expression
post-decrement-expression
pre-increment-expression
pre-decrement-expression
await-expression
By doing (foo.a)(), syntactically you are taking the () out of the 'scope'/context of (foo.a)
Meaning, you are calling foo.a, which is syntactically invalid, then calling () on the result of that operation.
In other words, asking for () on the result of the expression (foo.a), rather than requesting a() of foo.
Edit: See comments
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With