为什么在lambda语句周围加上括号会导致语法错误?

10
我希望您能提供一个好的解释,说明为什么一段代码无法编译,而另一段代码可以顺利编译。
失败的代码:
richTextBox1.Invoke(new MethodInvoker((() => { richTextBox1.AppendText("test"); })));

出现错误:

期望方法名

MethodInvoker(后的左括号上,报错了。显然我不能把lambda语句放在括号中。

编译通过:

richTextBox1.Invoke(new MethodInvoker(() => { richTextBox1.AppendText("test"); }));

问题是 - 为什么?

我一直认为,如果需要的话,可以在任何方法参数周围加上括号,但显然对于lambda表达式并非如此。我知道它们有点特殊,但我仍然看不到一个好的理由。也许我对语法有所误解。我真的很想弄清楚。

顺便说一下,这在VS2008、.NET 3.5 SP1中存在,我还没有在VS2010和.NET 4中进行测试。

3个回答

7
这不是一个lambda表达式,而是包含lambda表达式的一个带括号的表达式。因此,在此方法调用的抽象语法树中,该参数的节点将是一个带括号的表达式,而不是规范所要求的lambda表达式。这就是原因。
还有其他一些地方,微软的C#编译器违反了规范并接受了这样的表达式(尽管按照规范不应该),但这不是其中之一。
相关规范的章节是§6.5。

谢谢,那很有道理。只是出于好奇 - 你能否举个这样的例子? - Dyppl
3
@Dyppl:Func<int> F() { return (() => 1); } 是合法的,但根据规范来说不应该这样做。 - jason
谢谢!那么,我对我的问题感觉好多了。这种不一致最终会让你感到困扰。 - Dyppl
5
实际上,C#编译器在许多情况下会默默地删除括号,即使这种做法不符合规范的严格解释。在绝大多数情况下,这不会产生影响,并且它确切地执行了用户预期的操作,因此我不会过于关注此事。实际上,我有点惊讶这确实会导致错误。 :-) - Eric Lippert

4
你在写“方法参数”这一前提上是错误的。你所创建的结构不是一个方法调用,而是一个委托创建表达式(请参阅C#规范第7.6.10.5节),它应该有一个单一的参数,该参数必须是以下之一:
  • 一个方法组,
  • 一个匿名函数或
  • 编译时类型为dynamic或委托类型的值。
在你的情况下,它既不是方法组(错误信息提示需要一个方法名),也不是匿名函数(因为它是一个包含匿名函数的表达式),也不是上述类型的值。
如果你写了一个方法调用,即使它包含lambda表达式,你确实可以将参数括在括号中。
void Method(Action action)
{
}
...
Method((() => { Console.WriteLine("OK"); }));

3

因为编译器期望在Invoke()方法内部有()=>{},在第一个例子中它并没有找到。括号内的所有内容首先被计算得出单个对象,此时编译器期望委托的引用。

编辑过的 我已经使用了这个扩展方法解决了相同的问题:

    public delegate void EmptyHandler();
    public static void SafeCall(this Control control, EmptyHandler method) 
    {
        if (control.InvokeRequired)
        {
            control.Invoke(method);
        }
        else
        {
            method();
        }
    } 

所以你可以调用。
RichTextBox rtb = new RichRextBox();
...

rtb.SafeCall( ()=> rtb.AppendText("test") );

3
实际上,匿名委托也是语法糖。C# 编译器并不是将 lambda 语法转换为匿名委托语法,而是将二者都转换为具体函数(或具有函数的类,具体取决于闭包)和传统委托。 - Adam Robinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接