Méthode anonyme dans l'appel Invoke

131

Avoir un peu de problèmes avec la syntaxe où nous voulons appeler un délégué de manière anonyme dans un Control.Invoke.

Nous avons essayé un certain nombre d'approches différentes, toutes en vain.

Par exemple:

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

où someParameter est local à cette méthode

Ce qui précède entraînera une erreur du compilateur:

Impossible de convertir la méthode anonyme en type «System.Delegate» car ce n'est pas un type délégué

Duncan
la source

Réponses:

221

Parce que Invoke/ BeginInvokeaccepte Delegate(plutôt qu'un délégué typé), vous devez indiquer au compilateur le type de délégué à créer; MethodInvoker(2.0) ou Action(3.5) sont des choix courants (notez qu'ils ont la même signature); ainsi:

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

Si vous avez besoin de passer des paramètres, alors les "variables capturées" sont le moyen:

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

(mise en garde: vous devez être un peu prudent si vous utilisez des captures asynchrones , mais la synchronisation est bien - c'est-à-dire que ce qui précède est bien)

Une autre option consiste à écrire une méthode d'extension:

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

puis:

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

Vous pouvez bien sûr faire de même avec BeginInvoke:

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

Si vous ne pouvez pas utiliser C # 3.0, vous pouvez faire la même chose avec une méthode d'instance régulière, probablement dans une Formclasse de base.

Marc Gravell
la source
Comment puis-je passer des paramètres à votre première solution dans cette réponse? Je voulais dire cette solution: control.Invoke ((MethodInvoker) delegate {this.Text = "Hi";});
uzay95
1
Pourquoi la méthode d'extension est-elle appelée sans avoir à effectuer une conversion explicite en action?
P.Brian.Mackey
Parce que le compilateur peut déduire cela de l'utilisation.
RoboJ1M
1
C'est la même chose que de pouvoir faire à la Form.Load += Loader()place de l'ancienForm.Load += new EventHandler(Loader())
RoboJ1M
49

En fait, vous n'avez pas besoin d'utiliser le mot-clé de délégué. Passez simplement lambda comme paramètre:

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
Vokinneberg
la source
16
myControl.Invoke(new MethodInvoker(delegate() {...}))
François
la source
13

Vous devez créer un type de délégué. Le mot-clé «délégué» dans la création de méthode anonyme est un peu trompeur. Vous ne créez pas un délégué anonyme mais une méthode anonyme. La méthode que vous avez créée peut être utilisée dans un délégué. Comme ça:

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

Par souci d'exhaustivité, cela peut également être accompli via une combinaison méthode Action / méthode anonyme:

//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
la source
Invoke((Action) Process);est la meilleure réponse, merci!
Jinjinov
5

J'ai eu des problèmes avec les autres suggestions car je veux parfois renvoyer des valeurs de mes méthodes. Si vous essayez d'utiliser MethodInvoker avec des valeurs de retour, cela ne semble pas plaire. Donc, la solution que j'utilise est comme ça (très heureux d'entendre un moyen de rendre cela plus succinct - j'utilise 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;
        }));
    }
Rory
la source
1

J'aime utiliser Action à la place de MethodInvoker, il est plus court et semble plus propre.

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

// OR

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

Par exemple.

// 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";
}
Du D.
la source
0

Je n'ai jamais compris pourquoi cela fait une différence pour le compilateur, mais c'est suffisant.

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

Bonus: ajoutez une gestion des erreurs, car il est probable que, si vous utilisez à Control.Invokepartir d'un thread d'arrière-plan, vous mettez à jour l'état de texte / progression / activé d'un contrôle et ne vous souciez pas si le contrôle est déjà supprimé.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
Jürgen Steinblock
la source