我正在审核我的同事的一些WPF代码,这是一个库的基于UserControl
的组件,其中有很多 async void
事件和命令处理程序。这些方法目前内部没有实现任何错误处理。
代码简而言之:
<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
)。显然,这不是一个好主意。
我可以在本地处理异常:
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不应该总是作为库代码的一部分。
另一种方法是在本地处理它们并触发专门的错误事件:
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的体验有限。
是否有已建立的WPF模式将错误从async void
命令处理程序传播到ViewModal?
在WPF命令处理程序中执行异步工作通常是个坏主意,因为它们可能用于快速同步UI更新?
我在WPF的上下文中提出这个问题,但我认为它也适用于WinForms中的async void
事件处理程序。
答案 0 :(得分:7)
这里的问题是您的UserControl库没有以典型的MVVM方式构建。通常,对于非平凡的命令,您的UserControl的代码不会直接绑定到命令,而是具有在设置(通过绑定到ViewModel)时将触发控件中的操作的属性。然后,您的ViewModel将绑定到应用程序命令,并设置适当的属性。 (或者,您的MVVM框架可能有另一个消息传递方案,可用于ViewModel和View之间的交互。)
至于UI中引发的异常,我再次感到存在架构问题。如果UserControl不仅仅充当View,(即运行可能导致意外异常的任何类型的业务逻辑),那么这应该分为View和ViewModel。 ViewModel将运行逻辑,可以由其他应用程序ViewModel实例化,也可以通过其他方法进行通信(如上所述)。
如果UserControl的布局/可视化代码抛出了异常,那么ViewModel不会以任何方式捕获(几乎没有例外)。正如您所提到的,这应该仅由全局级别处理程序进行日志记录处理。
最后,如果Control的代码中确实存在已知的“异常”,则需要通知您的ViewModel,我建议捕获已知异常并引发事件/命令并设置属性。但同样,这不应该用于例外,只是预期的“错误”状态。
答案 1 :(得分:4)
在我看来,用户几乎100%不知道的异常的传播并不是一个好习惯。见this
我看到了你真正拥有的两个选项,因为WPF没有提供任何开箱即用的机制来通知任何问题:
总而言之,你必须为这样的代码编写一些xml-comments,因为这并不容易理解。 最重要的是你不应该(几乎)从任何辅助线程中抛出任何异常。