web-dev-qa-db-ja.com

非汎用TaskCompletionSourceまたは代替

私は通常、非同期に表示されるアラートウィンドウ(Telerik WPF)で作業しています(開いている間はコードが実行され続けます)。async/ awaitを使用して同期させたいです。

私はこれをTaskCompletionSourceで動作させていますが、そのクラスは汎用であり、戻り値のない単純なTaskだけが必要なときにTask<bool>のようなオブジェクトを返します。

public Task<bool> ShowAlert(object message, string windowTitle)
{
    var dialogParameters = new DialogParameters { Content = message };

    var tcs = new TaskCompletionSource<bool>();
    dialogParameters.Closed += (s, e) => tcs.TrySetResult(true);

    RadWindow.Alert(dialogParameters);

    return tcs.Task;
}

そのメソッドを呼び出すコードは

await MessageBoxService.ShowAlert("The alert text.")

dialogParameters.Closedイベントが発生するまで待機できる、同様に機能する非汎用タスクを返すにはどうすればよいですか?このコードで返されるboolを無視できることを理解しています。私はそれとは異なる解決策を探しています。

60

メソッドは次のように変更できます。

public Task ShowAlert(object message, string windowTitle)

Task<bool>Taskを継承するため、Task<bool>Taskのみを呼び出し元に公開します

編集:

Stephen Toubによる「The-based-based Asynchronous pattern」というタイトルのMicrosoftドキュメント http://www.Microsoft.com/en-us/download/details.aspx?id=19957 を見つけました。この同じパターンを推奨する次の抜粋があります。

TaskCompletionSource <TResult>に対応する非ジェネリック版はありません。ただし、Task <TResult>はTaskから派生しているため、一般的なTaskCompletionSource <TResult>は、ダミーのTResultを持つソースを利用して単純にTaskを返すI/Oにバインドされたメソッドに使用できます(ブールは適切なデフォルトの選択であり、開発者がTask <TResult>にダウンキャストするTaskのコンシューマーを心配している場合、プライベートTResultタイプを使用できます)

85

情報を漏らしたくない場合、一般的なアプローチはTaskCompletionSource<object>およびnullの結果で完了します。次に、それをTaskとして返します。

45
Stephen Cleary

Nito.AsyncEx は、上記の@StephenCleary氏の功績により、非ジェネリックTaskCompletionSourceクラスを実装します。

3
georgiosd

@Kevin Kalitowskiから

スティーブントーブによる「The Task-based Asynchronous pattern」というタイトルのMicrosoftドキュメント http://www.Microsoft.com/en-us/download/details.aspx?id=19957 を見つけました。

このドキュメントには、Kevinが指摘しているように、この問題を扱う例があります。これは例です:

public static Task Delay(int millisecondsTimeout)
{
    var tcs = new TaskCompletionSource<bool>();
    new Timer(self =>
    {
        ((IDisposable)self).Dispose();
        tcs.TrySetResult(true);
    }).Change(millisecondsTimeout, -1);
    return tcs.Task;
}

最初は、コンパイルメッセージなしではメソッドに「async」修飾子を直接追加できないため、良くないと思いました。ただし、メソッドを少し変更すると、メソッドはasync/awaitでコンパイルされます。

public async static Task Delay(int millisecondsTimeout)
{
    var tcs = new TaskCompletionSource<bool>();
    new Timer(self =>
    {
        ((IDisposable)self).Dispose();
        tcs.TrySetResult(true);
    }).Change(millisecondsTimeout, -1);
    await tcs.Task;
}

編集:最初はこぶを乗り越えたと思った。しかし、アプリで同等のコードを実行すると、このコードはtcs.Task;を待機するときにアプリをハングさせるだけです。ですから、これはasync/await c#構文の重大な設計上の欠陥だとまだ信じています。

1