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, возвращаемый в этом коде. Я ищу другое решение.


person Kevin Kalitowski    schedule 15.08.2012    source источник


Ответы (5)


Метод можно изменить на:

public Task ShowAlert(object message, string windowTitle)

Task<bool> наследуется от Task, поэтому вы можете вернуть Task<bool>, открывая только Task вызывающему

Изменить:

Я нашел документ Microsoft, http://www.microsoft.com/en-us/download/details.aspx?id=19957 Стивена Туба под названием «Асинхронный шаблон, основанный на задачах», и в нем есть следующий отрывок, который рекомендует этот же шаблон.

Нет неуниверсального аналога TaskCompletionSource ‹TResult>. Однако Task ‹TResult> является производным от Task, и поэтому общий TaskCompletionSource ‹TResult> может использоваться для методов, связанных с вводом-выводом, которые просто возвращают Task, используя источник с фиктивным TResult (Boolean - хороший выбор по умолчанию, и если разработчик обеспокоен тем, что потребитель Задачи преобразует ее в Задачу ‹TResult>, может использоваться частный тип TResult)

person Kevin Kalitowski    schedule 15.08.2012

Если вы не хотите утечки информации, общий подход состоит в том, чтобы использовать TaskCompletionSource<object> и получить результат null. Затем просто верните его как Task.

person Stephen Cleary    schedule 15.08.2012

.NET 5 имеет неуниверсальный TaskCompletionSource.

Он был добавлен в этот запрос на перенос: https://github.com/dotnet/runtime/pull/37452/files#diff-4a72dcb26e2d643c337baef9f64312f3

person Drew Noakes    schedule 12.08.2020

Nito.AsyncEx реализует неуниверсальный класс TaskCompletionSource, кредит господину @StephenCleary выше.

person georgiosd    schedule 25.07.2015

От @Kevin Kalitowski

Я нашел документ 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 / 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 #.

person Christian Findlay    schedule 22.08.2016