webentwicklung-frage-antwort-db.com.de

Anonyme Methode in Aufruf aufrufen

Probleme mit der Syntax, bei der wir einen Delegierten anonym innerhalb eines Control.Invoke anrufen möchten.

Wir haben verschiedene Ansätze ausprobiert, die alle erfolglos waren.

Zum Beispiel:

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

dabei ist someParameter für diese Methode lokal

Das obige wird zu einem Compiler-Fehler führen:

Eine anonyme Methode kann nicht in den Typ 'System.Delegate' konvertiert werden, da es sich nicht um einen Delegattyp handelt

121
Duncan

Da Invoke/BeginInvokeDelegate akzeptiert (und nicht einen typisierten Delegaten), müssen Sie dem Compiler mitteilen, welche Art von Delegat erstellt werden soll. MethodInvoker (2.0) oder Action (3.5) sind gängige Möglichkeiten (beachten Sie, dass sie dieselbe Signatur haben); wie so:

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

Wenn Sie Parameter übergeben müssen, sind "erfasste Variablen" der Weg:

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

(Vorbehalt: Wenn Sie Captures async verwenden, müssen Sie etwas vorsichtig sein, aber sync ist in Ordnung - d.

Eine weitere Möglichkeit ist, eine Erweiterungsmethode zu schreiben:

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

dann:

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

Das können Sie natürlich auch mit BeginInvoke:

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

Wenn Sie C # 3.0 nicht verwenden können, können Sie dasselbe mit einer regulären Instanzmethode tun, vermutlich in einer Form-Basisklasse.

212
Marc Gravell

Eigentlich brauchen Sie kein Delegate-Keyword. Übergeben Sie einfach Lambda als Parameter:

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

Sie müssen einen Delegattyp erstellen. Das Schlüsselwort 'delegate' bei der anonymen Methodenerstellung ist etwas irreführend. Sie erstellen keinen anonymen Delegaten, sondern eine anonyme Methode. Die von Ihnen erstellte Methode kann in einem Delegaten verwendet werden. So was:

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

Der Vollständigkeit halber kann dies auch über eine Kombination aus Action-Methode und anonymer Methode erfolgen:

//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

Ich hatte Probleme mit den anderen Vorschlägen, weil ich manchmal Werte aus meinen Methoden zurückgeben möchte. Wenn Sie versuchen, MethodInvoker mit Rückgabewerten zu verwenden, scheint es nicht zu gefallen. Die Lösung, die ich verwende, ist also so (sehr glücklich, einen Weg zu finden, um dies noch prägnanter zu machen - ich verwende 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

Ich habe nie verstanden, warum dies für den Compiler einen Unterschied macht, aber das reicht aus.

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

Bonus: Fügen Sie einige Fehlerbehandlung hinzu, da Sie wahrscheinlich den Text/Fortschritt/Aktivierungsstatus eines Steuerelements aktualisieren, wenn Sie Control.Invoke aus einem Hintergrund-Thread verwenden, und sich nicht darum kümmern, ob das Steuerelement bereits entsorgt wurde.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
0