为什么以这种方式调用Action成员的语法是无效的?

本文关键字:语法 无效 成员 Action 方式 调用 为什么 | 更新日期: 2023-09-27 18:10:26

下面的代码产生一个语法错误:

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;为什么我不能叫它?

为什么以这种方式调用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)结果上请求(),而不是请求fooa()

编辑:参见注释