非泛型 TaskCompletionSource 或替代方案

我使用的警报窗口(Telerik WPF)通常是异步显示的(代码在打开时继续运行) ,我希望通过使用异步/等待来实现同步。

我有这个工作与 TaskCompletionSource,但该类是通用的,并返回一个对象,如 Task<bool>时,我想要的是一个纯粹的 Task没有返回值。

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。我正在寻找一个不同的解决方案。

28696 次浏览

The method can be changed to:

public Task ShowAlert(object message, string windowTitle)

Task<bool> inherits from Task so you can return Task<bool> while only exposing Task to the caller

Edit:

I found a Microsoft document, http://www.microsoft.com/en-us/download/details.aspx?id=19957, by Stephen Toub titled 'The Task-based Asynchronous pattern' and it has the following excerpt that recommends this same pattern.

There is no non-generic counterpart to TaskCompletionSource<TResult>. However, Task<TResult> derives from Task, and thus the generic TaskCompletionSource<TResult> can be used for I/O-bound methods that simply return a Task by utilizing a source with a dummy TResult (Boolean is a good default choice, and if a developer is concerned about a consumer of the Task downcasting it to a Task<TResult>, a private TResult type may be used)

If you don't want to leak information, the common approach is to use TaskCompletionSource<object> and complete with a result of null. Then just return it as a Task.

Nito.AsyncEx implements a non-generic TaskCompletionSource class, credit to Mr @StephenCleary above.

From @Kevin Kalitowski

I found a Microsoft document, http://www.microsoft.com/en-us/download/details.aspx?id=19957, by Stephen Toub titled 'The Task-based Asynchronous pattern'

There is an example in this document which I think deals with the issue as Kevin points out. This is the example:

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;
}

At first I thought it was not good because you can't directly add the "async" modifier to the method without a compilation message. But, if you change the method slightly, the method will compile with 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;
}

Edit: at first I thought I'd gotten over the hump. But, when I ran the equivalent code in my app, this code just makes the app hang when it gets to await tcs.Task;. So, I still believe that this is a serious design flaw in the async/await c# syntax.

.NET 5 has a non-generic TaskCompletionSource.

It was added in this pull request: https://github.com/dotnet/runtime/pull/37452/files#diff-4a72dcb26e2d643c337baef9f64312f3