O início não pode ser chamado em uma tarefa do tipo promessa. exceção está chegando

108

Estou criando um aplicativo de desktop wpf simples. A IU tem apenas um botão e um código em arquivo .cs como.

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}

public void FunctionA()
{
    Task.Delay(5000).Start();
    MessageBox.Show("Waiting Complete");
}

Mas, surpreendentemente, a linha Task.Delay(5000).Start();está lançando um InvalidOperationException:

O início não pode ser chamado em uma tarefa do tipo promessa.

Alguém pode ajudar por que é assim?

DJ
fonte

Respostas:

171

Você está recebendo esse erro porque a Taskclasse já iniciou a tarefa antes de entregá-la a você. Você só deve chamar Startuma tarefa que cria chamando seu construtor, e nem mesmo deve fazer isso, a menos que tenha um motivo convincente para não iniciar a tarefa ao criá-la; se quiser que ele comece imediatamente, você deve usar Task.Runou Task.Factory.StartNewpara criar e iniciar um novo Task.

Então, agora sabemos apenas nos livrar desse incômodo Start. Você executará seu código e verá que a caixa de mensagem é exibida imediatamente, não 5 segundos depois, o que há com isso?

Bem, Task.Delayapenas lhe dá uma tarefa que será concluída em 5 segundos. Ele não para a execução do thread por 5 segundos. O que você deseja fazer é executar algum código após o término da tarefa. É para isso que ContinueWithserve. Ele permite que você execute alguns códigos após a conclusão de uma determinada tarefa:

public void FunctionA()
{
    Task.Delay(5000)
    .ContinueWith(t => 
    {
        MessageBox.Show("Waiting Complete");
    });
}

Isso se comportará conforme o esperado.

Também poderíamos aproveitar a awaitpalavra-chave do C # 5.0 para adicionar continuações mais facilmente:

public async Task FunctionA()
{
    await Task.Delay(5000);
    MessageBox.Show("Waiting Complete");
}

Embora uma explicação completa do que está acontecendo aqui esteja além do escopo desta pergunta, o resultado final é um método que se comporta de maneira muito semelhante ao método anterior; ele mostrará uma caixa de mensagem 5 segundos após você chamar o método, mas o método em si retornará [quase] imediatamente em ambos os casos. Dito isso, awaité muito poderoso e nos permite escrever métodos que parecem simples e diretos, mas que seriam muito mais difíceis e complicados de escrever usando ContinueWithdiretamente. Ele também simplifica muito o tratamento de erros, retirando muito código clichê.

Servy
fonte
1

Experimente isso.

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}

public async void FunctionA()
{
    await Task.Delay(5000);
    MessageBox.Show("Waiting Complete");
}
Mathias Lykkegaard Lorenzen
fonte
-4

Como o Servy disse, a tarefa já começou, então tudo o que você precisa fazer é esperar (.Wait ()):

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}
public void FunctionA()
{
    Task.Delay(5000).Wait();
    MessageBox.Show("Waiting Complete");
}
Sergiu
fonte
1
Chamar Wait()uma tarefa bloqueará o thread atual até que a tarefa seja resolvida. Quase nunca é isso que você quer que aconteça.
Jeremy
1
@Jeremy: Na verdade, vale a pena prestar atenção ao comportamento que você mencionou, mas, neste caso, sua FunctionA já estava bloqueando o thread atual, então presumi que ele está apenas procurando uma maneira de determinar quando a tarefa foi concluída. Para esclarecer a diferença entre Wait e async (para leitores futuros), leia este link
Sergiu