Pergunta

Ter um pouco de dificuldade com a sintaxe onde queremos chamar um delegado anonimamente dentro de um Control.Invoke.

Temos tentado um número de diferentes abordagens, todos sem sucesso.

Por exemplo:

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

onde someParameter é local para esse método

O código acima irá resultar em um erro do compilador:

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

Foi útil?

Solução

Porque Invoke / BeginInvoke aceita Delegate (em vez de um delegado digitado), você precisa dizer ao compilador que tipo de delegado para criar; MethodInvoker (2.0) ou Action (3,5) são escolhas comuns (note-se que eles têm a mesma assinatura); como assim:

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

Se você precisa passar parâmetros, "variáveis ??capturados", em seguida, são a maneira:

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

(ressalva: você precisa ser um pouco cauteloso se usando capturas de assíncrono , mas sync é bom - 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"; });

Você pode, naturalmente, fazer o mesmo com BeginInvoke:

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

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

Outras dicas

Na verdade, você não precisa de palavras-chave uso delegado. Basta passar lambda como parâmetro:

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

Você precisa criar um tipo delegado. A palavra-chave 'delegado' na criação método anônimo é um pouco enganador. 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 esta:

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

Por uma questão de exaustividade, este também pode ser realizado através de uma combinação de método de método de ação / 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();
});

Eu tive problemas com as outras sugestões, porque eu quero voltar, por vezes, valores de meus métodos. Se você tentar usar MethodInvoker com valores de retorno que não parece como ele. Assim, o uso solução que é assim (muito feliz ao ouvir uma maneira de fazer isso mais sucinta - Eu estou 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;
        }));
    }

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

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

Bonus: adicionar um pouco de manipulação de erro, porque é provável que, se você estiver usando Control.Invoke a partir de uma discussão de fundo que está a actualizar o texto / progresso / estado de um controle habilitado e não se importam se o controle já é descartado.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top