Non-Generic TaskCompletionSource или альтернативный
Я работаю с окном предупреждения (Telerik WPF), которое обычно отображается асинхронно (код продолжает работать, пока он открыт), и я хочу сделать его синхронным с помощью async/await.
У меня это работает с 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
, который возвращается в этом коде. Я ищу другое решение, чем это.
Ответы
Ответ 1
Метод можно изменить на:
public Task ShowAlert(object message, string windowTitle)
Task<bool>
наследует от Task
, поэтому вы можете возвращать Task<bool>
, только вызывая Task
вызывающему
Edit:
Я нашел документ Microsoft, http://www.microsoft.com/en-us/download/details.aspx?id=19957, Стивен Туб под названием "Асинхронный шаблон на основе задач" и имеет следующий отрывок который рекомендует эту же модель.
Нет никакого универсального аналога TaskCompletionSource <TResult> . Однако Task <TResult> происходит из задачи, и поэтому общий TaskCompletionSource <TResult> может использоваться для связанных с I/O методов, которые просто возвращают Задачу, используя источник с фиктивным TResult (Boolean является хорошим выбором по умолчанию и если разработчик обеспокоен тем, что потребитель задачи опущен в Task <TResult> , может использоваться частный тип TResult)
Ответ 2
Если вы не хотите утечки информации, общий подход заключается в использовании TaskCompletionSource<object>
и завершении с результатом null
. Затем просто верните его как Task
.
Ответ 3
Nito.AsyncEx реализует неэквивалентный класс TaskCompletionSource
, относящийся к Mr @StephenCleary выше.
Ответ 4
На самом деле существуют две версии "не общего" TaskCompletionSource
для реализации. При гипотетическом имени типа они могут пониматься как TaskCompletionSource<Unit>
и TaskCompletionSource<Bottom>
.
Разница двух состоит в том, что первый должен определить a SetResult
без параметра, а в дальнейшем не будет определять SetResult
вообще. Это приведет к невозможности выполнить Task
of TaskCompletionSource<Bottom>
в обычном режиме, но вы можете отменить или установить его в течение некоторого времени.
Оба варианта имеют свои варианты использования, но я думаю, что Microsoft не принимала решения о стандартизации любого из них.
Вот пример реализации
public class TaskCompletionSourceUnit {
private TaskCompletionSource<int> tcs;
public TaskCompletionSourceUnit() { tcs = new TaskCompletionSource<int>(); }
public TaskCompletionSourceUnit(TaskCreationOptions creationOptions) { tcs = new TaskCompletionSource<int>(creationOptions); }
public TaskCompletionSourceUnit(object state) { tcs = new TaskCompletionSource<int>(state); }
public TaskCompletionSourceUnit(object state, TaskCreationOptions creationOptions) { tcs = new TaskCompletionSource<int>(state, creationOptions); }
public Task Task { get { return tcs.Task; } }
public void SetCanceled() { tcs.SetCanceled(); }
public void SetException(Exception exception) { tcs.SetException(exception); }
public void SetException(IEnumerable<Exception> exceptions) { tcs.SetException(exceptions); }
public void SetResult() { tcs.SetResult(0); }
public bool TrySetCanceled() { return tcs.TrySetCanceled(); }
public bool TrySetException(Exception exception) { return tcs.TrySetException(exception); }
public bool TrySetException(IEnumerable<Exception> exceptions) { return tcs.TrySetException(exceptions); }
public bool TrySetResult() { return tcs.TrySetResult(0); }
}
public class TaskCompletionSourceBottom
{
private TaskCompletionSource<int> tcs;
public TaskCompletionSourceBottom() { tcs = new TaskCompletionSource<int>(); }
public TaskCompletionSourceBottom(TaskCreationOptions creationOptions) { tcs = new TaskCompletionSource<int>(creationOptions); }
public TaskCompletionSourceBottom(object state) { tcs = new TaskCompletionSource<int>(state); }
public TaskCompletionSourceBottom(object state, TaskCreationOptions creationOptions) { tcs = new TaskCompletionSource<int>(state, creationOptions); }
public Task Task { get { return tcs.Task; } }
public void SetCanceled() { tcs.SetCanceled(); }
public void SetException(Exception exception) { tcs.SetException(exception); }
public void SetException(IEnumerable<Exception> exceptions) { tcs.SetException(exceptions); }
public bool TrySetCanceled() { return tcs.TrySetCanceled(); }
public bool TrySetException(Exception exception) { return tcs.TrySetException(exception); }
public bool TrySetException(IEnumerable<Exception> exceptions) { return tcs.TrySetException(exceptions); }
}
Ответ 5
От @Кевина Калитовского
Я нашел документ Microsoft, http://www.microsoft.com/en-us/download/details.aspx?id=19957, Стивен Туб под названием "Асинхронный шаблон на основе задач"
В этом документе есть пример, который, я думаю, касается вопроса, как указывает Кевин. Это пример:
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/wait:
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 С#.