Método anônimo na chamada de chamada

131

Tendo um pouco de problema com a sintaxe em que queremos chamar um delegado anonimamente dentro de um Control.Invoke.

Tentamos várias abordagens diferentes, todas sem sucesso.

Por exemplo:

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

onde someParameter é local para esse método

O exemplo acima resultará em um erro do compilador:

Não é possível converter o método anônimo para digitar 'System.Delegate' porque não é um tipo de delegado

Duncan
fonte

Respostas:

221

Como Invoke/ BeginInvokeaceita Delegate(em vez de um delegado digitado), você precisa informar ao compilador que tipo de delegado criar; MethodInvoker(2.0) ou Action(3.5) são escolhas comuns (observe que elas têm a mesma assinatura); igual a:

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

Se você precisar passar parâmetros, "variáveis ​​capturadas" são as seguintes:

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

(ressalva: você precisa ser um pouco cauteloso se usar capturas assíncronas , mas sincronize é boa - ou seja, o acima é bom)

Outra opção é escrever um método de extensão:

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

então:

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

Obviamente, você pode fazer o mesmo com BeginInvoke:

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

Se você não pode usar o C # 3.0, pode fazer o mesmo com um método de instância regular, presumivelmente em uma Formclasse base.

Marc Gravell
fonte
Como posso passar parâmetros para sua primeira solução nesta resposta? Eu quis dizer esta solução: control.Invoke ((MethodInvoker) delegate {this.Text = "Hi";});
uzay95
1
Por que o método de extensão é chamado sem ter que fazer uma conversão explícita para a ação?
usar o seguinte código
Porque o compilador pode inferir isso do uso.
21416 RoboJ1M
1
É o mesmo que ser capaz de fazer Form.Load += Loader()em vez do antigoForm.Load += new EventHandler(Loader())
RoboJ1M
49

Na verdade, você não precisa usar a palavra-chave delegada. Apenas passe lambda como parâmetro:

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

Você precisa criar um tipo de delegado. A palavra-chave 'delegar' na criação do método anônimo é um pouco enganadora. Você não está criando um delegado anônimo, mas um método anônimo. O método que você criou pode ser usado em um delegado. Como isso:

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

Por uma questão de integridade, isso também pode ser realizado por meio de uma combinação de método Action / 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
fonte
Invoke((Action) Process);é a melhor resposta, obrigado!
Jinjinov 19/03
5

Eu tive problemas com as outras sugestões porque às vezes quero retornar valores dos meus métodos. Se você tentar usar o MethodInvoker com valores de retorno, não parece gostar. Portanto, a solução que uso é a seguinte (muito feliz em ouvir uma maneira de tornar isso mais sucinto - estou usando o 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
fonte
1

Eu gosto de usar o Action no lugar do MethodInvoker, é mais curto e parece mais limpo.

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

// OR

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

Por exemplo.

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

Eu nunca entendi por que isso faz diferença para o compilador, mas isso é suficiente.

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

Bônus: adicione algum tratamento de erro, porque é provável que, se você estiver usando Control.Invokede um encadeamento em segundo plano, esteja atualizando o texto / progresso / estado ativado de um controle e não se importe se o controle já estiver descartado.

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
fonte