问题描述
想象一下WPF代码隐藏事件处理程序:
Imagine a WPF code-behind event handler:
<Button Click="OnButtonClick" />
在C#4中,您将声明处理程序为:
In C# 4 you would declare your handler as:
private void OnButtonClick(object sender, RoutedEventArgs e) { ... }
在C#5中,您可以声明async
处理程序
In C# 5 you can declare an async
handler
private async void OnButtonClick(object sender, RoutedEventArgs e) { ... }
那么WPF对此做了什么?几分钟的搜索并没有发现任何问题.
So what is WPF doing with this? A few minutes of searching about didn't turn anything up.
似乎可以在await
语句之后执行UI更新.这是否意味着任务将在Dispatcher线程上继续执行?
It seems that it's possible to perform UI updates after await
statements. Does this imply that the task is continued on the Dispatcher thread?
如果Task
引发错误,是通过WPF Dispatcher
还是仅通过TaskScheduler
引发?
If the Task
raised an error, would it be raised through the WPF Dispatcher
, or only via the TaskScheduler
?
对此是否还有其他有趣的方面值得理解?
Are there any other interesting aspects to this that might be nice to understand?
推荐答案
您可能会发现我的异步/等待简介很有帮助.
You may find my async/await intro helpful.
async
方法由编译器重写,以支持await
运算符.每个async
方法开始同步(在这种情况下,在UI线程上),直到它await
进行某些操作(尚未完成)为止.
An async
method is re-written by the compiler to support the await
operator. Every async
method starts out synchronous (in this case, on the UI thread) until it await
s some operation (that is not already completed).
默认情况下,将保存上下文,并且当操作完成时,该方法的其余部分计划在该上下文中执行.除非为null
,否则此处的上下文"为SynchronizationContext.Current
,在这种情况下为TaskScheduler.Current
.正如Drew所指出的那样,WPF提供了一个DispatcherSynchronizationContext
,该DispatcherSynchronizationContext
与WPF Dispatcher
相关.
By default, the context is saved, and when the operation completes, the rest of the method is scheduled to execute in that context. The "context" here is SynchronizationContext.Current
unless it is null
, in which case it is TaskScheduler.Current
. As Drew pointed out, WPF provides a DispatcherSynchronizationContext
which is tied to the WPF Dispatcher
.
关于错误处理:
当您在WPF async void
事件处理程序中await
一个Task
时,错误处理如下:
When you await
a Task
inside a WPF async void
event handler, the error handling goes like this:
-
Task
完成时出现错误.像所有Task
错误一样,该异常也包装在AggregateException
中. -
await
操作员看到Task
完成但有错误.它将取消包装原始异常并重新引发它,从而保留原始堆栈跟踪. -
async void
方法构建器捕获从async void
方法转义的异常,并将其传递到async void
方法开始执行时处于活动状态的SynchronizationContext
(在这种情况下,相同的WPF上下文)./li> - 在
Dispatcher
上引发了异常(具有原始堆栈跟踪,并且没有任何令人讨厌的AggregateException
包装).
- The
Task
completes with an error. The exception is wrapped into anAggregateException
, like allTask
errors. - The
await
operator sees that theTask
completed with an error. It unwraps the original exception and re-throws it, preserving the original stack trace. - The
async void
method builder catches the exception escaping from anasync void
method and passes it to theSynchronizationContext
that was active when theasync void
method started executing (in this case, the same WPF context). - The exception is raised (with the original stack trace, and without any annoying
AggregateException
wrapping) on theDispatcher
.
这相当复杂,但是目的是使从async
事件处理程序引发的异常与从常规事件处理程序引发的异常几乎相同.
This is rather convoluted, but the intent is to have exceptions raised from async
event handlers be practically the same as exceptions raised from regular event handlers.
这篇关于在C#5中将WPF事件处理程序声明为“异步"的意义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!