Анонимный метод в вызове Invoke

131

Возникли некоторые проблемы с синтаксисом, когда мы хотим анонимно вызвать делегата в Control.Invoke.

Мы испробовали несколько разных подходов, но безуспешно.

Например:

myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(someParameter)); }); 

где someParameter является локальным для этого метода

Вышеупомянутое приведет к ошибке компилятора:

Невозможно преобразовать анонимный метод в тип System.Delegate, потому что это не тип делегата

Дункан
источник

Ответы:

221

Поскольку Invoke/ BeginInvokeпринимает Delegate(а не типизированный делегат), вам необходимо сообщить компилятору, какой тип делегата нужно создать; MethodInvoker(2.0) или Action(3.5) - обычные варианты (обратите внимание, что у них одинаковая подпись); вот так:

control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});

Если вам нужно передать параметры, то подходящими являются "захваченные переменные":

string message = "Hi";
control.Invoke((MethodInvoker) delegate {this.Text = message;});

(предостережение: вам нужно быть немного осторожным при использовании асинхронных захватов , но синхронизация в порядке - то есть вышеупомянутое в порядке)

Другой вариант - написать метод расширения:

public static void Invoke(this Control control, Action action)
{
    control.Invoke((Delegate)action);
}

затем:

this.Invoke(delegate { this.Text = "hi"; });
// or since we are using C# 3.0
this.Invoke(() => { this.Text = "hi"; });

Конечно, вы можете сделать то же самое с BeginInvoke:

public static void BeginInvoke(this Control control, Action action)
{
    control.BeginInvoke((Delegate)action);
}

Если вы не можете использовать C # 3.0, вы можете сделать то же самое с обычным методом экземпляра, предположительно в Formбазовом классе.

Марк Гравелл
источник
Как я могу передать параметры вашему первому решению в этом ответе? Я имел в виду такое решение: control.Invoke ((MethodInvoker) delegate {this.Text = "Hi";});
uzay95
1
Почему вызывается метод расширения без явного приведения к действию?
P.Brian.Mackey
Поскольку компилятор может сделать это из использования.
RoboJ1M
1
Это то же самое, что уметь делать Form.Load += Loader()вместо старогоForm.Load += new EventHandler(Loader())
RoboJ1M
49

На самом деле вам не нужно использовать ключевое слово делегата. Просто передайте лямбда в качестве параметра:

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
Vokinneberg
источник
16
myControl.Invoke(new MethodInvoker(delegate() {...}))
Франсуа
источник
13

Вам нужно создать тип делегата. Ключевое слово «делегат» при создании анонимного метода немного вводит в заблуждение. Вы создаете не анонимного делегата, а анонимный метод. Созданный вами метод можно использовать в делегате. Как это:

myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));
Желон
источник
8

Для полноты картины это также можно сделать с помощью комбинации метода Action / анонимного метода:

//Process is a method, invoked as a method group
Dispatcher.Current.BeginInvoke((Action) Process);
//or use an anonymous method
Dispatcher.Current.BeginInvoke((Action)delegate => {
  SomeFunc();
  SomeOtherFunc();
});
mhamrah
источник
Invoke((Action) Process);лучший ответ, спасибо!
Джинджинов
5

У меня были проблемы с другими предложениями, потому что я иногда хочу возвращать значения из моих методов. Если вы попытаетесь использовать MethodInvoker с возвращаемыми значениями, ему это не понравится. Итак, решение, которое я использую, похоже на это (очень рад услышать способ сделать это более лаконичным - я использую c # .net 2.0):

    // Create delegates for the different return types needed.
    private delegate void VoidDelegate();
    private delegate Boolean ReturnBooleanDelegate();
    private delegate Hashtable ReturnHashtableDelegate();

    // Now use the delegates and the delegate() keyword to create 
    // an anonymous method as required

    // Here a case where there's no value returned:
    public void SetTitle(string title)
    {
        myWindow.Invoke(new VoidDelegate(delegate()
        {
            myWindow.Text = title;
        }));
    }

    // Here's an example of a value being returned
    public Hashtable CurrentlyLoadedDocs()
    {
        return (Hashtable)myWindow.Invoke(new ReturnHashtableDelegate(delegate()
        {
            return myWindow.CurrentlyLoadedDocs;
        }));
    }
Рори
источник
1

Мне нравится использовать Action вместо MethodInvoker, он короче и выглядит чище.

Invoke((Action)(() => {
    DoSomething();
}));

// OR

Invoke((Action)delegate {
    DoSomething();
});

Например.

// Thread-safe update on a form control
public void DisplayResult(string text){
    if (txtResult.InvokeRequired){
        txtResult.Invoke((Action)delegate {
            DisplayResult(text);
        });
        return;
    }

    txtResult.Text += text + "\r\n";
}
Ду Д.
источник
0

Я никогда не понимал, почему это имеет значение для компилятора, но этого достаточно.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        control.Invoke(action);
    }
}

Бонус: добавьте некоторую обработку ошибок, потому что, вероятно, если вы используете Control.Invokeиз фонового потока, вы обновляете текст / прогресс / состояние элемента управления и не заботитесь о том, что элемент управления уже удален.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
Юрген Штайнблок
источник