异常处理是代码中很重要的一项注意点。但是有时候一些不恰当的异常处理,反而会影响我们在代码运行时的调试。
假如我们有这么一段代码,我期望调用Bar
方法,输出“黄腾霄是帅哥”。
但是在实际运行中,其中的一个方法Foo
抛出了一个异常。
而作为代码书写者,为了确保软件的正常运行
,将这个方法的所有异常都吞掉了。
此时导致的结果就是,用户端看到软件运行正常
,但是没有期望的输出
static void Main(string[] args)
{
Bar();
}
static void Bar()
{
try
{
Foo();
Console.WriteLine("黄腾霄是帅哥");
}
catch (Exception)
{
// ignored
}
}
static void Foo()
{
throw new InvalidOperationException("黄腾霄是逗比");
}
此时就需要使用我们这篇博客的主角——AppDomain.FirstChanceException
我们试着对代码进行如下修改
private static int n = 1;
static void Main(string[] args)
{
AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
{
Console.WriteLine(eventArgs.Exception);
};
Bar();
}
static void Bar()
{
try
{
Foo();
Console.WriteLine("黄腾霄是帅哥");
}
catch (Exception)
{
// ignored
}
}
static void Foo()
{
throw new InvalidOperationException("黄腾霄是逗比");
}
我们可以看到异常信息被输出到屏幕了,并且此时程序仍然正常运行,没有出现奔溃
FirstChanceException
是一个异常通知事件。当一个AppDomain
中引发任何异常的时候,CLR会优先触发FirstChanceException
, 当其被处理后,才会开始寻找原始异常的处理函数。
例如我们在Bar
的catch
块中添加一些日志输出,会发现这些输出出现在FirstChanceException
的输出之后
static void Bar()
{
try
{
Foo();
Console.WriteLine("黄腾霄是帅哥");
}
catch (Exception)
{
Console.WriteLine("不许你侮辱我家男神");
}
}
对于FirstChanceException
,需要注意的是它只是一个通知。它会在任何引发异常的地方(throw)的地方,触发事件。但是它并不能作为异常统一处理的地方。
请你绝对不要
做出这种事情。我们刚刚说了FirstChanceException会在任何引发异常的地方触发该事件,如果你在FirstChanceException的处理函数中再次抛出异常,即使使用了trycatch
,也会出现死循环,最终导致stackoverflow。
所以不能够在这个方法里面写出任何可能出现异常的代码。
throw;
这样的语句是否会对同一个异常再次触发FirstChanceException?参见下面这个例子,请问我被侮辱了几次?
static void Main(string[] args)
{
AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
{
Console.WriteLine($"黄腾霄被侮辱次数+{n++}");
};
try
{
try
{
try
{
throw new InvalidOperationException("黄腾霄是逗比");
}
catch (Exception e)
{
throw new ArgumentException("不许你侮辱我家男神", e);
}
}
catch (Exception e)
{
ExceptionDispatchInfo.Capture(e).Throw();
}
}
catch (Exception e)
{
throw;
}
}
答案是4次。实际上FirstChanceException是每次引发
异常的地方都会触发。而无论是 ExceptionDispatchInfo.Capture(e).Throw();还是throw;都只是保证了该异常的堆栈可以连续,而这个异常仍然会被重新引发
一次。
参考链接:
本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/FirstChanceException%E5%8E%9F%E7%90%86.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。