Método anónimo en invocar llamada

131

Tener un poco de problemas con la sintaxis donde queremos llamar a un delegado de forma anónima dentro de un Control.Invoke.

Hemos probado varios enfoques diferentes, todos en vano.

Por ejemplo:

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

donde someParameter es local para este método

Lo anterior dará como resultado un error del compilador:

No se puede convertir el método anónimo para escribir 'System.Delegate' porque no es un tipo de delegado

Duncan
fuente

Respuestas:

221

Como Invoke/ BeginInvokeacepta Delegate(en lugar de un delegado con tipo), debe decirle al compilador qué tipo de delegado crear; MethodInvoker(2.0) o Action(3.5) son opciones comunes (tenga en cuenta que tienen la misma firma); al igual que:

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

Si necesita pasar parámetros, las "variables capturadas" son las siguientes:

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

(advertencia: debe ser un poco cauteloso si usa capturas asíncronas , pero la sincronización está bien, es decir, lo anterior está bien)

Otra opción es escribir un método de extensión:

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

luego:

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

Por supuesto, puedes hacer lo mismo con BeginInvoke:

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

Si no puede usar C # 3.0, puede hacer lo mismo con un método de instancia regular, presumiblemente en una Formclase base.

Marc Gravell
fuente
¿Cómo puedo pasar parámetros a su primera solución en esta respuesta? Me refería a esta solución: control.Invoke ((MethodInvoker) delegate {this.Text = "Hi";});
uzay95
1
¿Por qué se invoca el Método de extensión sin tener que hacer una conversión explícita a la acción?
P.Brian.Mackey
Porque el compilador puede inferir eso del uso.
RoboJ1M
1
Es lo mismo que poder hacer en Form.Load += Loader()lugar de lo viejoForm.Load += new EventHandler(Loader())
RoboJ1M
49

En realidad, no necesita usar la palabra clave delegado. Simplemente pase lambda como parámetro:

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

Necesita crear un tipo de delegado. La palabra clave 'delegar' en la creación del método anónimo es un poco engañosa. No está creando un delegado anónimo, sino un método anónimo. El método que creó puede usarse en un delegado. Me gusta esto:

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

En aras de la integridad, esto también se puede lograr a través de una combinación de método de acción / método anónimo:

//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
fuente
Invoke((Action) Process);es la mejor respuesta, gracias!
Jinjinov
5

Tuve problemas con las otras sugerencias porque a veces quiero devolver valores de mis métodos. Si intenta utilizar MethodInvoker con valores de retorno, no parece que le guste. Entonces, la solución que uso es así (muy feliz de escuchar una manera de hacer esto más sucinto: estoy 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;
        }));
    }
Rory
fuente
1

Me gusta usar Action en lugar de MethodInvoker, es más corto y se ve más limpio.

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

// OR

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

P.ej.

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

Nunca entendí por qué esto hace una diferencia para el compilador, pero esto es suficiente.

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

Bonificación: agregue algo de manejo de errores, porque es probable que, si está utilizando Control.Invokeun subproceso en segundo plano, esté actualizando el texto / progreso / estado habilitado de un control y no le importe si el control ya está dispuesto.

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
fuente