Metodo anonimo nella chiamata Invoke


131

Avere un po 'di problemi con la sintassi in cui vogliamo chiamare un delegato in modo anonimo all'interno di Control.Invoke.

Abbiamo provato una serie di approcci diversi, tutti inutilmente.

Per esempio:

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

dove someParameter è locale a questo metodo

Quanto sopra comporterà un errore del compilatore:

Impossibile convertire il metodo anonimo per digitare "System.Delegate" perché non è un tipo delegato

Risposte:


221

Perché Invoke/ BeginInvokeaccetta Delegate(anziché un delegato tipizzato), è necessario indicare al compilatore quale tipo di delegato creare; MethodInvoker(2.0) o Action(3.5) sono scelte comuni (nota che hanno la stessa firma); così:

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

Se è necessario passare parametri, le "variabili acquisite" sono le seguenti:

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

(avvertenza: è necessario essere un po 'cauti se si utilizzano acquisizioni asincrone , ma la sincronizzazione va bene, ovvero quanto sopra va bene)

Un'altra opzione è quella di scrivere un metodo di estensione:

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

poi:

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

Ovviamente puoi fare lo stesso con BeginInvoke:

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

Se non puoi usare C # 3.0, puoi fare lo stesso con un metodo di istanza regolare, presumibilmente in una Formclasse base.


Come posso passare i parametri alla tua prima soluzione in questa risposta? Intendevo questa soluzione: control.Invoke ((MethodInvoker) delegate {this.Text = "Hi";});
uzay95,

1
Perché il metodo di estensione viene invocato senza dover eseguire un cast esplicito su Action?
P.Brian.Mackey,

Perché il compilatore può dedurlo dall'uso.
RoboJ1M,

1
È lo stesso che essere in grado di fare al Form.Load += Loader()posto del vecchioForm.Load += new EventHandler(Loader())
RoboJ1M,

49

In realtà non è necessario utilizzare la parola chiave delegata. Basta passare lambda come parametro:

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


13

Devi creare un tipo delegato. La parola chiave "delegato" nella creazione del metodo anonimo è un po 'fuorviante. Non stai creando un delegato anonimo ma un metodo anonimo. Il metodo creato può essere utilizzato in un delegato. Come questo:

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

8

Per completezza, questo può essere realizzato anche tramite una combinazione di metodo Action / metodo anonimo:

//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);è la migliore risposta, grazie!
Jinjinov il

5

Ho avuto problemi con gli altri suggerimenti perché a volte voglio restituire valori dai miei metodi. Se si tenta di utilizzare MethodInvoker con valori restituiti, non sembra gradirlo. Quindi la soluzione che uso è così (molto felice di sentire un modo per renderlo più conciso - sto usando 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

Mi piace usare Action al posto di MethodInvoker, è più corto e sembra più pulito.

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

// OR

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

Per esempio.

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

Non ho mai capito perché questo faccia la differenza per il compilatore, ma questo è sufficiente.

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

Bonus: aggiungi un po 'di gestione degli errori, perché è probabile che, se stai usando Control.Invokeda un thread in background, stai aggiornando lo stato di testo / avanzamento / abilitato di un controllo e non ti importa se il controllo è già eliminato.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.