Anonyme Methode in Aufruf aufrufen

131

Probleme mit der Syntax, bei der ein Delegierter innerhalb eines Control.Invoke anonym angerufen werden soll.

Wir haben verschiedene Ansätze ausprobiert, alle ohne Erfolg.

Beispielsweise:

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

Dabei ist someParameter für diese Methode lokal

Das Obige führt zu einem Compilerfehler:

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

Duncan
quelle

Antworten:

221

Da Invoke/ BeginInvokeakzeptiert Delegate(anstelle eines typisierten Delegaten), müssen Sie dem Compiler mitteilen, welcher Delegatentyp erstellt werden soll. MethodInvoker(2.0) oder Action(3.5) sind gängige Optionen (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 richtige Weg:

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

(Einschränkung: Sie müssen etwas vorsichtig sein, wenn Sie asynchrone Captures verwenden , aber die Synchronisierung ist in Ordnung - dh die oben genannten sind in Ordnung.)

Eine andere Möglichkeit besteht darin, 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"; });

Sie können das natürlich auch tun 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 FormBasisklasse.

Marc Gravell
quelle
Wie kann ich in dieser Antwort Parameter an Ihre erste Lösung übergeben? Ich meinte diese Lösung: control.Invoke ((MethodInvoker) delegate {this.Text = "Hi";});
uzay95
1
Warum wird die Erweiterungsmethode aufgerufen, ohne dass eine explizite Umwandlung in eine Aktion durchgeführt werden muss?
P.Brian.Mackey
Weil der Compiler das aus der Verwendung ableiten kann.
RoboJ1M
1
Es ist das gleiche wie in der Lage zu sein, Form.Load += Loader()anstelle des altenForm.Load += new EventHandler(Loader())
RoboJ1M
49

Eigentlich müssen Sie kein delegate-Schlüsselwort verwenden. Übergeben Sie einfach Lambda als Parameter:

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

Sie müssen einen Delegatentyp erstellen. Das Schlüsselwort 'delegate' bei der Erstellung anonymer Methoden 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)); }));
Jelon
quelle
8

Der Vollständigkeit halber kann dies auch über eine Kombination aus Aktionsmethode 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();
});
Mhamrah
quelle
Invoke((Action) Process);ist die beste Antwort, danke!
Jinjinov
5

Ich hatte Probleme mit den anderen Vorschlägen, weil ich manchmal Werte von meinen Methoden zurückgeben möchte. Wenn Sie versuchen, MethodInvoker mit Rückgabewerten zu verwenden, scheint es Ihnen nicht zu gefallen. Die Lösung, die ich verwende, ist also wie folgt (sehr erfreut zu hören, wie man dies prägnanter macht - 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;
        }));
    }
Rory
quelle
1

Ich verwende Action gerne anstelle von MethodInvoker, es ist kürzer und sieht sauberer aus.

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

// OR

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

Z.B.

// 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.
quelle
0

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

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

Bonus: Fügen Sie eine Fehlerbehandlung hinzu, da es wahrscheinlich ist, dass Sie bei Verwendung Control.Invokeeines Hintergrundthreads den Text- / Fortschritts- / Aktivierungsstatus eines Steuerelements aktualisieren und es egal ist, ob das Steuerelement bereits entsorgt ist.

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
quelle