web-dev-qa-db-fra.com

Méthode anonyme dans l'appel Invoke

Avoir un petit problème avec la syntaxe où nous voulons appeler un délégué anonymement dans un Control.Invoke.

Nous avons essayé plusieurs 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 de compilation:

Impossible de convertir une méthode anonyme en type 'System.Delegate' car il ne s'agit pas d'un type de délégué

121
Duncan

Étant donné que Invoke/BeginInvoke accepte 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 chemin:

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

(mise en garde: vous devez être un peu prudent si vous utilisez captures async , mais sync est correct - c'est-à-dire que cela est correct)

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 classe de base Form.

212
Marc Gravell

En fait, vous n'avez pas besoin d'utiliser le mot clé délégué. Il suffit de passer lambda en paramètre:

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

Vous devez créer un type de délégué. Le mot clé 'delegate' dans la création de la 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)); }));
13
Jelon

À des fins d’exhaustivité, ceci peut également être réalisé 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();
});
6
mhamrah

J'ai eu des problèmes avec les autres suggestions parce que 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 vous plaire. La solution que j’utilise est donc la suivante (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;
        }));
    }
5
Rory

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 un peu de gestion des erreurs, car il est probable que si vous utilisez Control.Invoke à partir d'un fil de travail en arrière-plan, vous mettez à jour l'état text/progress/enabled d'un contrôle et ne vous souciez pas de savoir s'il 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) { }
    }
}
0
Jürgen Steinblock