Подтвердить что ты не робот

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

4b9b3361

Ответ 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 С#.