在C#5中将WPF事件处理程序声明为"异步"的意义

Dre*_*kes 19 .net c# wpf asynchronous c#-5.0

想象一下WPF代码隐藏事件处理程序:

<Button Click="OnButtonClick" />
Run Code Online (Sandbox Code Playgroud)

在C#4中,您将把处理程序声明为:

private void OnButtonClick(object sender, RoutedEventArgs e) { ... }
Run Code Online (Sandbox Code Playgroud)

在C#5中,您可以声明一个async处理程序

private async void OnButtonClick(object sender, RoutedEventArgs e) { ... }
Run Code Online (Sandbox Code Playgroud)

那么WPF在做什么呢?几分钟的搜索没有改变任何东西.

似乎可以在await语句之后执行UI更新.这是否意味着任务在Dispatcher线程上继续?

如果Task引发错误,是通过WPF引发的Dispatcher,还是仅通过TaskScheduler

还有其他有趣的方面可能很难理解吗?

Ste*_*ary 24

您可能会发现我的异步/等待介绍很有帮助.

async编译器重写一个方法以支持await运算符.每个async方法都开始同步(在这种情况下,在UI线程上),直到它进行await某些操作(尚未完成).

默认情况下,保存上下文,当操作完成时,方法的其余部分将安排在该上下文中执行.这里的"上下文" SynchronizationContext.Current除非它是null,在这种情况下它是TaskScheduler.Current.正如Drew指出的那样,WPF提供了一个DispatcherSynchronizationContext与WPF相关的东西Dispatcher.

关于错误处理:

当你await一个Task一个WPF中async void的事件处理程序,错误处理是这样的:

  • Task出错完成.AggregateException像所有Task错误一样,异常被包装成一个.
  • await操作员发现的Task完成了一个错误.它解开原始异常并重新抛出它,保留原始堆栈跟踪.
  • async void方法构建器捕捉异常从逸出async void的方法和把它传递给SynchronizationContext处于活动状态时,async void方法开始执行(在这种情况下,相同的WPF上下文).
  • 引发异常(使用原始堆栈跟踪,没有任何烦人的AggregateException包装)Dispatcher.

这是相当复杂的,但目的是使async事件处理程序引发的异常与常规事件处理程序引发的异常几乎相同.


Dre*_*kes 6

部分答案。从MSDN

无法等待具有 void 返回类型的异步方法,并且返回 void 的方法的调用者无法捕获该方法抛出的任何异常。

因此,任何错误都只能通过TaskScheduler.

此外,事件处理程序注册没有任何特定于 XAML 的内容。它可以在代码中完成:

this.button.Click += OnButtonClick;
Run Code Online (Sandbox Code Playgroud)

甚至作为异步 lambda:

this.button.Click += async (s,e) => { ... };
Run Code Online (Sandbox Code Playgroud)

至于 之后 UI 更新的安全性await,似乎在 内执行延续SynchronisationContext.Current,这是每个线程设置的。在 WPF 中,这是与首先抽取事件DispatcherSynchronisationContext的 WPF 耦合的Dispatcher