Анонимные методы и делегаты

Я пытаюсь понять, почему метод BeginInvoke не принимает анонимный метод.

void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (InvokeRequired)
    {
        //Won't compile
        BeginInvoke(delegate(object sender, ProgressChangedEventArgs e) 
        { bgWorker_ProgressChanged(sender, e); });
    }

    progressBar1.Increment(e.ProgressPercentage);
}

Он говорит мне: "Невозможно преобразовать из" анонимного метода "в" System.Delegate ", а когда я передаю анонимный метод делегату, он работает?

BeginInvoke((progressDelegate)delegate { bgWorker_ProgressChanged(sender, e); });

Ответ 1

Класс Delegate является базовым классом для типов делегатов. Однако только система и компиляторы могут быть получены явно из класса Delegate или из класса MulticastDelegate. Также недопустимо выводить новый тип из типа делегата. Класс Delegate не считается типом делегата; это класс, используемый для получения типов делегатов. Источник - MSDN

Следовательно, необходимо, чтобы явный приведение к типу производного от делегата. Вы столкнулись с этой конкретной ошибкой компилятора, когда вы передаете анонимный метод для параметра типа System.Delegate - к счастью, это редкий сценарий. Это слишком большая гибкость.

delegate void MyDelegate();

  static void DoSomething_Flexible(Delegate d)
  {   d.DynamicInvoke();      }
  static void DoSomething_Usable(MyDelegate d)
  {   d();      }
  static void Main(string[] args)
  {
     // requires explicit cast else compile error Error "Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type    
     DoSomething_Flexible((MyDelegate) delegate { Console.WriteLine("Flexible is here!"); });  

     // Parameter Type is a .NET Delegate, no explicit cast needed here. 
     DoSomething_Usable(delegate { Console.WriteLine("Usable is here!"); });
  }

Подробнее об этом на этой странице Ян Гриффит. (См. Парас после заголовка Notes)

Ответ 2

Вам нужно сообщить компилятору, какой тип делегата создать, так как Invoke (и т.д.) просто возьмите Delegate (а не что-то более конкретное).

Чтобы применить к самой большой аудитории, MethodInvoker - удобный тип делегата

BeginInvoke((MethodInvoker) delegate(...) {...});

Однако... BackgroundWorker.ProgressChanged автоматически запускается в потоке пользовательского интерфейса, поэтому вам это даже не нужно.

Ответ 3

В большинстве случаев вы имеете дело с делегатом без параметров или с предикатом в этих случаях. Самый простой способ его сортировки заключается в том, чтобы направить ваш анонимный метод непосредственно на Action или Predicate соответственно; вам просто не нужно создавать пользовательский тип делегата для таких простых вещей.

Итак, у вас будет что-то вроде

BeginInvoke((Action)delegate(){YourCode.DoSomething();});

или

BeginInvoke((Predicate)delegate(object yourParameter){return YourCode.IsTheParameterSomething(yourParameter)});

НТН