Почему круглые скобки вокруг оператора лямбда вызывают синтаксическую ошибку?

Я ищу хорошее объяснение, почему одна часть кода не компилируется, а другая компилируется просто.

Не удается:

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

Дает ошибку

Ожидаемое имя метода

в открывающей скобке справа после MethodInvoker(. Видимо, я не могу обернуть свои лямбда-выражения в круглые скобки.

Собирает:

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

Вопросы: почему?

Я всегда считал само собой разумеющимся, что я мог бы обернуть любой параметр метода в круглые скобки, если захочу, но, по-видимому, это не так, как с лямбда-выражениями. Я понимаю, что они несколько особенные, но я до сих пор не вижу достаточной причины для этого. Может быть, я ничего не понимаю о синтаксисе. Я действительно хотел бы получить его.

Кстати, это представлено в VS2008,.NET 3.5 SP1, я еще не тестировал его в VS2010 и .NET 4.

Ответ 1

Это не лямбда-выражение, это выражение в скобках, содержащее выражение лямбда. Следовательно, node для этого параметра в дереве абстрактного синтаксиса для этого вызова метода будет выражением в скобках, а не выражением лямбда, как того требует спецификация. Вот почему.

Существуют и другие места, где компилятор Microsoft С# нарушает спецификацию и принимает такое выражение, даже если оно не должно (в спецификации), но это не один из них.

Соответствующий раздел спецификации - §6.5.

Ответ 2

Вы ошибаетесь в предположении, что вы написали "параметр метода". Созданная вами конструкция не является вызовом метода, вы создали выражение для создания делегата (см. Спецификацию С#, раздел 7.6.10.5), который должен иметь один аргумент, который должен быть либо

  • группа методов,
  • анонимная функция или
  • значение динамического типа времени компиляции или типа делегата.

В вашем случае это не группа методов (сообщение об ошибке указывает, что там ожидается имя метода) или анонимная функция (поскольку это выражение, которое "где-то внутри" содержит анонимную функцию), а также значение указанных типов.

Если вы написали метод invokation, вы могли бы действительно обернуть этот параметр в круглые скобки, даже если он содержит выражение лямбда:

void Method(Action action)
{
}
...
Method((() => { Console.WriteLine("OK"); }));

Ответ 3

Поскольку компилятор ожидает ()=>{} внутри метода Invoke(), а в первом примере он его не находит. Все в скобках оценивается сначала, возвращая один объект, и в этом случае компилятор ожидает ссылки на делегат.

Edited Я решил ту же проблему с этим методом расширения:

    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") );