下面的代码产生一个语法错误:
class Foo
{
public Action a = () => { };
}
void doSomething()
{
var foo = new Foo();
(foo.a)(); // error CS1525: Invalid expression term ')'
}
但是,以下选项都可以使用:
foo.a(); // works
Action a = foo.a; a(); // works
为什么会这样?(foo.a)
是Action
;为什么我不能叫它?
发生了什么?
这是因为代码(foo.a)();
将计算为类型转换表达式,因为foo.a
可以是成员访问或类型。这就是为什么编译器在空方括号内搜索表达式的原因。
Func<string, int> fn = null;
int x = (fn)("asd"); // The type or namespace "fn" could not be found
在这里,编译器明确地声明它将其解释为类型转换表达式,因为它期望fn
是类型。
因此,在第一组括号中,不能有任何可以被读为类型的,因为这会导致整个表达式被读为类型转换表达式。
编译以下代码段:
(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))();
但是这些行没有:
(Console.WriteLine)("asd");
Action a = null;
(a)();
Action<string> b = null;
(b)("");
为什么会这样?
原来的句法语法是有歧义的。为什么解析器将(a)()
解释为类型转换表达式,而不是类型转换表达式,但它可以将其解释为调用表达式?解析器何时决定是强制转换还是调用?嗯,他们想到了这个。规范规定(§7.7.6):
强制转换表达式的语法会导致某些语法歧义。例如,表达式(x) -y可以被解释为强制转换表达式(将-y转换为类型x)还是作为加性表达式的组合使用带括号的表达式(用于计算值x - y)。
为了解决转换表达式的歧义,存在以下规则:一个或多个令牌的序列(第2.3.3节)只有当以下条件中至少有一个为真时,用圆括号括起来的才被认为是强制转换表达式的开始:
- 符号序列对类型来说是正确的语法,但对表达式来说不是。
- 标记序列是类型的正确语法,并且紧接在结束符之后的标记括号是记号"~",记号"!",令牌"(",标识符(§2.4.1),文字(§2.4.4),或任何关键字(§2.4.3)除了as和is。
在本例中,"标记序列对于一个类型来说是正确的语法,紧跟在右括号后面的标记是标记(
"所代表的,所以这就是故事的结尾。
句法语法
来自c# 5.0语言规范
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
通过执行(foo.a)()
,在语法上您将()
从(foo.a)
的'范围'/上下文中取出
意思是,你正在调用foo.a
,这在语法上是无效的,然后调用()
对该操作的结果。
换句话说,在表达式(foo.a)
的结果上请求()
,而不是请求foo
的a()
。
编辑:参见注释