Анонімний метод у виклику Invoke


131

Проблеми з синтаксисом, коли ми хочемо викликати делегата анонімно в Control.Invoke.

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

Наприклад:

myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(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;});

(застереження: вам потрібно бути обережними, якщо ви використовуєте функції захоплення async , але синхронізація нормальна - тобто вище сказано)

Ще один варіант - написати метод розширення:

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) делегат {this.Text = "Привіт";});
uzay95

1
Чому метод розширення викликається, не виконуючи явну роль у дії?
P.Brian.Mackey

Тому що компілятор може зробити це з використання.
RoboJ1M

1
Це те саме, що можна робити Form.Load += Loader()замість старогоForm.Load += new EventHandler(Loader())
RoboJ1M

49

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

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


13

Потрібно створити тип делегата. Ключове слово "делегат" у створенні анонімного методу трохи вводить в оману. Ви створюєте не анонімного делегата, а анонімний метод. Створений вами метод може бути використаний у делегаті. Подобається це:

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

8

Для повноти це також можна досягти за допомогою комбінації методу Дія / анонімний метод:

//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();
});

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) { }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.