此代码抛出异常。是否可以定义一个应用程序全局处理程序来捕获它?
string x = await DoSomethingAsync();
使用 .net 4.5/WPF
最佳答案
如果我理解正确的话,这实际上是一个好问题。我最初投票决定关闭它,但现在撤回了我的投票。
了解在 async Task
方法中抛出的异常如何传播到它之外很重要。最重要的是,处理任务完成的代码需要观察到此类异常。
例如,这是一个简单的 WPF 应用程序,我在 NET 4.5.1 上:
using System;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApplication_22369179
{
public partial class MainWindow : Window
{
Task _task;
public MainWindow()
{
InitializeComponent();
AppDomain.CurrentDomain.UnhandledException +=
CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException +=
TaskScheduler_UnobservedTaskException;
_task = DoAsync();
}
async Task DoAsync()
{
await Task.Delay(1000);
MessageBox.Show("Before throwing...");
GCAsync(); // fire-and-forget the GC
throw new ApplicationException("Surprise");
}
async void GCAsync()
{
await Task.Delay(1000);
MessageBox.Show("Before GC...");
// garbage-collect the task without observing its exception
_task = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
}
void TaskScheduler_UnobservedTaskException(object sender,
UnobservedTaskExceptionEventArgs e)
{
MessageBox.Show("TaskScheduler_UnobservedTaskException:" +
e.Exception.Message);
}
void CurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
MessageBox.Show("CurrentDomain_UnhandledException:" +
((Exception)e.ExceptionObject).Message);
}
}
}
一旦 ApplicationException
被抛出,它就不会被观察到。 TaskScheduler_UnobservedTaskException
和 CurrentDomain_UnhandledException
都不会被调用。异常保持休眠状态,直到 _task
对象被等待或等待。在上面的示例中,它永远不会被观察到,因此 TaskScheduler_UnobservedTaskException
将仅在任务被垃圾收集时调用。那么这个异常将被吞噬。
旧的 .NET 4.0 行为,其中 AppDomain.CurrentDomain.UnhandledException
事件被触发并且应用程序崩溃,可以通过配置 ThrowUnobservedTaskExceptions
启用。在 app.config
中:
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
以这种方式启用时,当异常被垃圾收集时,AppDomain.CurrentDomain.UnhandledException
仍将在 TaskScheduler.UnobservedTaskException
之后触发,而不是在它抛出的地方。
Stephen Toub 在他的 "Task Exception Handling in .NET 4.5" 中描述了这种行为博客文章。关于任务垃圾收集的部分在帖子的评论中进行了描述。
async Task
方法就是这种情况。对于通常用于事件处理程序的 async void
方法来说情况就完全不同了。让我们这样更改代码:
public MainWindow()
{
InitializeComponent();
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
this.Loaded += MainWindow_Loaded;
}
async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(1000);
MessageBox.Show("Before throwing...");
throw new ApplicationException("Surprise");
}
因为它是 async void
,所以没有要保留的 Task
引用(因此以后没有可能观察到或垃圾收集的内容)。在这种情况下,异常会立即在当前同步上下文中抛出。对于 WPF 应用程序,将首先触发 Dispatcher.UnhandledException
,然后是 Application.Current.DispatcherUnhandledException
,然后是 AppDomain.CurrentDomain.UnhandledException
。最后,如果这些事件都没有得到处理(EventArgs.Handled
未设置为 true
),应用程序将崩溃,无论 ThrowUnobservedTaskExceptions
环境。 TaskScheduler.UnobservedTaskException
在这种情况下不会被解雇,原因相同:没有Task
。
关于c# - TAP 全局异常处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22369179/