“async void"内部的异常处理WPF 命令处理程序 [英] Exception handling inside "async void" WPF command handlers

查看:36
本文介绍了“async void"内部的异常处理WPF 命令处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在审查我同事的一些 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆