Domanda

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.

Ad esempio:

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

dove someParameter è locale in questo metodo

Quanto sopra comporterà un errore del compilatore:

  

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

È stato utile?

Soluzione

Poiché Invoke / BeginInvoke accetta Delegate (anziché un delegato digitato), è 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); in questo modo:

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

Se devi passare i parametri, allora " variabili acquisite " sono il modo:

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

(avvertenza: devi essere un po 'cauto se usi le acquisizioni asincrono , ma sincronizza va bene - vale a dire quanto sopra va bene)

Un'altra opzione è scrivere un metodo di estensione:

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

quindi:

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

Altri suggerimenti

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

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

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. In questo modo:

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

Per completezza, ciò 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();
});

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;
        }));
    }

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.Invoke da un thread in background stai aggiornando il testo / progress / stato abilitato di un controllo e don' non 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) { }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top