Pregunta

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

Hemos intentado una serie de enfoques diferentes, todo en vano.

Por ejemplo:

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

donde algún parámetro es local a este método

Lo anterior generará un error de compilación:

  

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

¿Fue útil?

Solución

Debido a que Invoke / BeginInvoke acepta Delegate (en lugar de un delegado tecleado), debe decirle al compilador qué tipo de delegado debe crear. ; MethodInvoker (2.0) o Action (3.5) son opciones comunes (tenga en cuenta que tienen la misma firma); como asi:

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

Si necesita pasar parámetros, entonces " variables capturadas " son el camino:

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

(advertencia: debe ser un poco cauteloso si utiliza capturas async , pero sync 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);
}

entonces:

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

Por supuesto, puede 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, podría hacer lo mismo con un método de instancia regular, probablemente en una clase base Form .

Otros consejos

En realidad, no es necesario utilizar palabras clave delegadas. Solo pasa lambda como parámetro:

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

Necesitas 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ás creando un delegado anónimo sino un método anónimo. El método que creó se puede utilizar en un delegado. Así:

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

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

Tuve problemas con las otras sugerencias porque a veces quiero devolver valores de mis métodos. Si intentas usar MethodInvoker con valores de retorno, no parece gustarle. Así que la solución que utilizo es así (muy feliz de escuchar una manera de hacer esto más conciso, 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;
        }));
    }

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

Bonus: agregue un poco de manejo de errores, porque es probable que, si está utilizando Control.Invoke desde un hilo de fondo, esté actualizando el estado de texto / progreso / habilitado de un control y no lo haga. No importa 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) { }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top