“async void"内部的异常处理WPF 命令处理程序 [英] Exception handling inside "async void" WPF command handlers
问题描述
我正在审查我同事的一些 WPF 代码,这是一个基于 UserControl
的组件的库,其中包含很多 async void
事件和命令处理程序.这些方法目前在内部没有实现任何错误处理.
I'm reviewing some WPF code of my colleagues, which is a library of UserControl
-based components with a lot of async void
event and command handlers. These methods currently do not implement any error handling internally.
简单的代码:
<Window.CommandBindings>
<CommandBinding
Command="ApplicationCommands.New"
Executed="NewCommand_Executed"/>
</Window.CommandBindings>
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// do some fake async work (and may throw if timeout < -1)
var timeout = new Random(Environment.TickCount).Next(-100, 100);
await Task.Delay(timeout);
}
在NewCommand_Executed
中抛出但未观察到的异常只能在全局级别处理(例如,使用AppDomain.CurrentDomain.UnhandledException
).显然,这不是一个好主意.
Exceptions thrown but not observed inside NewCommand_Executed
can only be handled on a global level (e.g., with AppDomain.CurrentDomain.UnhandledException
). Apparently, this is not a good idea.
我可以在本地处理异常:
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
try
{
// do some fake async work (throws if timeout < -1)
var timeout = new Random(Environment.TickCount).Next(-100, 100);
await Task.Delay(timeout);
}
catch (Exception ex)
{
// somehow log and report the error
MessageBox.Show(ex.Message);
}
}
但是,在这种情况下,宿主应用程序的 ViewModel 不会意识到 NewCommand_Executed
中的错误.也不是理想的解决方案,而且错误报告 UI 不应该总是库代码的一部分.
However, in this case the host app's ViewModel would be unaware of errors inside NewCommand_Executed
. Not an ideal solution either, plus the error reporting UI shouldn't always be a part of the library code.
另一种方法是在本地处理它们并触发一个专用的错误事件:
public class AsyncErrorEventArgs: EventArgs
{
public object Sender { get; internal set; }
public ExecutedRoutedEventArgs Args { get; internal set; }
public ExceptionDispatchInfo ExceptionInfo { get; internal set; }
}
public delegate void AsyncErrorEventHandler(object sender, AsyncErrorEventArgs e);
public event AsyncErrorEventHandler AsyncErrorEvent;
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
ExceptionDispatchInfo exceptionInfo = null;
try
{
// do some fake async work (throws if timeout < -1)
var timeout = new Random(Environment.TickCount).Next(-100, 100);
await Task.Delay(timeout);
}
catch (Exception ex)
{
// capture the error
exceptionInfo = ExceptionDispatchInfo.Capture(ex);
}
if (exceptionInfo != null && this.AsyncErrorEvent != null)
this.AsyncErrorEvent(sender, new AsyncErrorEventArgs {
Sender = this, Args = e, ExceptionInfo = exceptionInfo });
}
我最喜欢最后一个,但我很感激任何其他建议,因为我在 WPF 方面的经验有限.
I like the last one the most, but I'd appreciate any other suggestions as my experience with WPF is somewhat limited.
是否有既定的 WPF 模式将错误从
async void
命令处理程序传播到 ViewModal?
Is there an established WPF pattern to propagate errors from
async void
command handlers to ViewModal?
在 WPF 命令处理程序中执行异步工作通常是个坏主意吗,因为它们可能用于快速同步 UI 更新?
Is it generally a bad idea to do async work inside WPF command handlers, as perhaps they're intended for quick synchronous UI updates?
我是在 WPF 的上下文中问这个问题的,但我认为它也适用于 WinForms 中的 async void
事件处理程序.
I'm asking this question in the context of WPF, but I think it may as well apply to async void
event handlers in WinForms.
推荐答案
这里的问题是您的 UserControl 库不是以典型的 MVVM 方式构建的.通常,对于重要的命令,您的 UserControl 代码不会直接绑定到命令,而是具有在设置时(通过绑定到 ViewModel)会触发控件中的操作的属性.然后您的 ViewModel 将绑定到应用程序命令,并设置适当的属性.(或者,您的 MVVM 框架可能有另一个消息传递场景,可用于 ViewModel 和 View 之间的交互).
The issue here is that your UserControl library is not architected in a Typical MVVM way. Commonly, for non-trivial commands, your UserControl's code would not bind to commands directly, but instead would have properties that when set (through binding to a ViewModel) would trigger the action in the control. Then your ViewModel would bind to the application command, and set the appropriate properties. (Alternatively, your MVVM framework may have another message passing scenario that can be leveraged for interaction between the ViewModel and View).
至于UI里面抛出的Exceptions,我又觉得是架构问题.如果 UserControl 不仅仅是充当视图(即运行可能导致意外异常的任何类型的业务逻辑),则应将其分为视图和视图模型.ViewModel 将运行逻辑,并且可以由您的其他应用程序 ViewModel 实例化,也可以通过其他方法进行通信(如上所述).
As for Exceptions that are thrown inside the UI, I again feel that there is an architecture issue. If the UserControl is doing more than acting as a View, (i.e. running any kind of business logic that might cause unanticipated exceptions) then this should be separated into a View and a ViewModel. The ViewModel would run the logic and could either be instantiated by your other application ViewModels, or communicate via another method (as mentioned above).
如果 UserControl 的布局/可视化代码抛出异常,那么您的 ViewModel 应该(几乎没有例外)以任何方式捕获异常.正如您所提到的,这应该仅由全局级别的处理程序处理以进行日志记录.
If there are exceptions being thrown by the UserControl's layout / visualization code then this should (almost without exception) not be caught in any way by your ViewModel. This should, as you mentioned, only be handled for logging by a global level handler.
最后,如果控件代码中确实存在需要通知您的 ViewModel 的已知异常",我建议捕获已知异常并引发事件/命令并设置属性.但同样,这真的不应该用于异常,只是预期的错误"状态.
Lastly, if there truly are known 'exceptions' in the Control's code that your ViewModel needs to be notified about, I suggest catching the known exceptions and raising an event/command and setting a property. But again, this really shouldn't be used for exceptions, just anticipated 'error' states.
这篇关于“async void"内部的异常处理WPF 命令处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!