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

Значение объявления обработчика события WPF как "async" в С# 5

Представьте, что обработчик событий с кодом для кода WPF:

<Button Click="OnButtonClick" />

В С# 4 вы объявите своего обработчика как:

private void OnButtonClick(object sender, RoutedEventArgs e) { ... }

В С# 5 вы можете объявить обработчик async

private async void OnButtonClick(object sender, RoutedEventArgs e) { ... }

Итак, что делает WPF с этим? Несколько минут поиска ничего не изменили.

Кажется, что возможно выполнять обновления пользовательского интерфейса после операторов await. Означает ли это, что задача продолжается в потоке диспетчера?

Если значение Task вызвало ошибку, оно было бы поднято через WPF Dispatcher или только через TaskScheduler?

Есть ли другие интересные аспекты, которые могут быть приятными для понимания?

4b9b3361

Ответ 1

Вы можете найти мой асинхронный/ждущий ввод.

Метод async переписывается компилятором для поддержки оператора await. Каждый метод async запускается синхронно (в этом случае в потоке пользовательского интерфейса), пока не будет await некоторая операция (которая еще не завершена).

По умолчанию контекст сохраняется, и когда операция завершается, остальная часть метода планируется выполнить в этом контексте. "Контекст" здесь SynchronizationContext.Current, если он не равен null, и в этом случае это TaskScheduler.Current. Как отметил Дрю, WPF предоставляет DispatcherSynchronizationContext, который привязан к WPF Dispatcher.

Что касается обработки ошибок:

Когда вы await a Task внутри обработчика событий WPF async void, обработка ошибок выглядит следующим образом:

  • Task завершается с ошибкой. Исключение завершается в AggregateException, как и все ошибки Task.
  • Оператор await видит, что Task завершен с ошибкой. Он разворачивает исходное исключение и перебрасывает его, сохраняя исходную трассировку стека.
  • Конструктор методов async void ловит исключение из метода async void и передает его в SynchronizationContext, который был активным, когда начал запускать метод async void (в данном случае тот же контекст WPF).
  • Исключение возникает (с исходной трассировкой стека и без какой-либо досадной упаковки AggregateException) на Dispatcher.

Это довольно запутанно, но намерение состоит в том, чтобы исключения, возникающие из обработчиков событий async, были практически такими же, как исключения, возникающие из обычных обработчиков событий.

Ответ 2

Частичный ответ. Из MSDN:

Асинхронный метод, который имеет тип возвращаемого типа void, не может быть ожидаемым, а вызывающий метод void-return не может поймать никаких исключений, которые вызывает метод.

Таким образом, любые ошибки будут доступны только через TaskScheduler.

Кроме того, нет ничего специфичного для XAML, связанного с регистрацией обработчика событий. Это можно было бы сделать в коде:

this.button.Click += OnButtonClick;

Или даже как асинхронная лямбда:

this.button.Click += async (s,e) => { ... };

Что касается безопасности обновлений пользовательского интерфейса после await, кажется, что продолжение выполняется в SynchronisationContext.Current, которое устанавливается за нить. В WPF это DispatcherSynchronisationContext, который связан с WPF Dispatcher, который накачивал событие в первую очередь.