Reputation: 6989
I'm working with an alert window (Telerik WPF) that is normally displayed asynchronously ( code continues running while it is open) and I want to make it synchronous by using async/await.
I have this working with TaskCompletionSource
but that class is generic and returns an object like Task<bool>
when all I want is a plain Task
with no return value.
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;
}
The code that calls that method is
await MessageBoxService.ShowAlert("The alert text.")
How can I return a non-generic Task that functions similarly which I can await until the dialogParameters.Closed
event fires? I understand that I could just ignore the bool
that is being returned in this code. I am looking for a different solution than that.
Upvotes: 94
Views: 32717
Reputation: 310832
.NET 5 has a non-generic TaskCompletionSource
.
It was added in this pull request: https://github.com/dotnet/runtime/pull/37452/files#diff-4a72dcb26e2d643c337baef9f64312f3
Upvotes: 24
Reputation: 7682
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.
Upvotes: 1
Reputation: 3059
Nito.AsyncEx implements a non-generic TaskCompletionSource
class, credit to Mr @StephenCleary above.
Upvotes: 3
Reputation: 6989
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)
Upvotes: 112
Reputation: 456397
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
.
Upvotes: 64