シンプルなwpfデスクトップアプリケーションを作成しています。 UIには、ボタンと.csファイルのようなコードがあります。
private void Button_Click_2(object sender, RoutedEventArgs e)
{
FunctionA();
}
public void FunctionA()
{
Task.Delay(5000).Start();
MessageBox.Show("Waiting Complete");
}
しかし、驚くべきことにTask.Delay(5000).Start();
行はInvalidOperationException
をスローしています:
開始は、promiseスタイルのタスクでは呼び出されない場合があります。
なぜこのようなことができるのですか?
Task
クラスがタスクを提供する前に既にタスクを開始しているため、このエラーが発生しています。コンストラクターを呼び出して作成するタスクでのみStart
を呼び出す必要があり、作成時にタスクを開始しないという説得力のある理由がない限り、それを行うべきではありません。すぐに開始したい場合は、Task.Run
またはTask.Factory.StartNew
を使用して、新しいTask
の作成と開始の両方を行う必要があります。
そのため、今では厄介なStart
を取り除くだけです。コードを実行すると、5秒後ではなく、メッセージボックスがすぐに表示されることがわかります。
Task.Delay
は、5秒で完了するタスクを提供します。 5秒間スレッドの実行を停止しません。あなたがしたいことは、そのタスクが完了した後に実行されるコードを持っていることです。それがContinueWith
の目的です。特定のタスクが完了した後、いくつかのコードを実行できます。
public void FunctionA()
{
Task.Delay(5000)
.ContinueWith(t =>
{
MessageBox.Show("Waiting Complete");
});
}
これは期待どおりに動作します。
C#5.0のawait
キーワードを活用して、継続をより簡単に追加することもできます。
public async Task FunctionA()
{
await Task.Delay(5000);
MessageBox.Show("Waiting Complete");
}
ここで何が起こっているかの完全な説明はこの質問の範囲を超えていますが、最終的な結果は以前の方法と非常によく似た方法です。メソッドを呼び出してから5秒後にメッセージボックスが表示されますが、どちらの場合もメソッド自体はすぐに(ほぼ)戻ります。そうは言っても、await
は非常に強力であり、シンプルでわかりやすいメソッドを記述できますが、ContinueWith
を直接使用して記述するのははるかに難しく、面倒です。また、エラー処理の処理を大幅に簡素化し、多くの定型コードを取り除きます。
これを試して。
private void Button_Click_2(object sender, RoutedEventArgs e)
{
FunctionA();
}
public async void FunctionA()
{
await Task.Delay(5000);
MessageBox.Show("Waiting Complete");
}