以下内容是从一款Windows 8 Metro/WinRT应用提取出来的片段,已经被精简到展现异常所需的最少程度:
public class App : Application
{
public App()
{
UnhandledException += (sender, e) => e.Handled = true;
}
}
public class MainPage : Page
{
private void Button_Click_1(object sender, RoutedEventArgs e)
{
throw new NotSupportedException();
}
private async void Button_Click_2(object sender, RoutedEventArgs e)
{
throw new NotSupportedException();
}
}
假设有一个Metro UI,其中包含两个按钮和它们各自的点击事件处理程序,唯一的区别是第二个事件处理程序被标记为async
。
然后,当我单击每个按钮时,我希望在两种情况下都会调用UnhandledException处理程序,因为它们(应该)都是通过UI线程和关联的同步上下文输入的。我的理解是,在async void
方法中,任何异常都应该通过最初的同步上下文捕获并“重新抛出”(保留原始堆栈跟踪),这也在Async / Await FAQ中明确说明。
但是,在async
情况下,UnhandledException处理程序没有被调用,因此应用程序崩溃了!由于这挑战了我认为非常直观的模型,我需要知道原因!是的,我知道可以将处理程序的主体包装在try { } catch { }
中,但我的问题是为什么没有调用最终的UnhandledException处理程序?
为了进一步强调为什么这不合理,考虑以下来自WPF应用程序的几乎相同的截取,该应用程序还使用了async / await并针对.NET Framework 4.5:
public class App : Application
{
public App()
{
DispatcherUnhandledException += (sender, e) => e.Handled = true;
}
}
public class MainWindow : Window
{
private void Button_Click_1(object sender, RoutedEventArgs e)
{
throw new NotSupportedException();
}
private async void Button_Click_2(object sender, RoutedEventArgs e)
{
throw new NotSupportedException();
}
}
async void
事件处理程序;即用try { } catch { }
块包装主体,或将主体作为 lambda 传递给帮助函数 - 该函数会包装 lambda 以避免重复。 - Sean Fausett