1.写在前面
“异常意味着什么?”,想必不用对此做多余的解释,我们有理由相信在任何情况下任何应用程序都有可能出现异常,若在程序中没有对异常进行处理,则操作系统会以粗暴的方式处理掉它(弹出错误提示框),在很多应用软件中都可以看到这样的提示框,无疑会影响用户的体验。所以,我们完全有必要对应用程序进行全方位的异常处理,尽可能地覆盖所有可能出现的异常,而程序的异常处理本身是一个非常重要却极易被忽视的问题。
2.一般的异常处理方式
无非就是try...catch...finally...的应用,把所有自认为可能出现异常的代码都用try块wrap起来,没错,这确实是一种完全可行的方法,但却不是最合适的做法。原因如下:
假如需要开发一个WinForm应用程序,首先生成项目时VS自动生成机器代码,然后编码人员进行编码,实现需要的功能,编码人员非常谨慎,所以他在自己编写的代码中添加了若干个异常处理块。。。无论怎么测试都没有出现未被捕获的异常,他很开心,可是有一天运行应用程序时还是发生了异常,这是为什么?答案是编码人员没错,VS生成的机器代码也没错,而是操作系统出错了,在加载该APP时可能遇到了Interruption,进而引发了异常(当然,只是举个例子)
我们可以看到,若撇开系统异常以及RunTimeException不讲,编码人员做得非常好,这样的做法是值得我们学习的,也必须养成这样的习惯。
3.对于一般异常处理的建议
在上面介绍的一般的异常处理方式中,我们不得不需要关注代码臃肿的问题,try...catch...finally...无疑会占用很大篇幅,这个是可以避免的,可以把异常尽量集中处理,推荐的做法是对下层的Exception直接throw出去(在函数定义时添上throws声明即可),在Main函数中统一处理(因为到这里就不能继续throw了,否则。。。)
其次在catch块处理异常时,我们可以新建一个独立的ExceptionClass定义各种异常的处理方法,避免出现大量冗余代码
每个线程都要对应一个异常处理方法,Main属于主线程,在自定义的子线程里应该使用独立的异常处理
4.集中式异常处理
上面的第一条建议只适合控制台应用程序,不存在复杂的人机交互,对于WinForm应用程序将不再适用,那么在WinForm应用程序中怎样合理地处理异常?
C Sharp的Application.ThreadException为我们提供了便利,用于处理UI线程异常,AppDomain.CurrentDomain.UnhandledException用于处理非UI线程异常
用法如下:
1 static class Program 2 { 3 /// <summary> 4 /// 应用程序的主入口点。 5 /// </summary> 6 [STAThread] 7 [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)] 8 static void Main() 9 { 10 Application.EnableVisualStyles(); 11 Application.SetCompatibleTextRenderingDefault(false); 12 //处理未捕获的异常,始终将异常传送到 ThreadException 处理程序 13 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 14 //集中处理异常 15 //订阅ThreadException事件,处理UI线程异常,处理方法为handler1 16 Application.ThreadException += new ThreadExceptionEventHandler(handler1); 17 //订阅UnhandledException事件,处理非UI线程异常,处理方法为handler2 18 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(handler2); 19 20 Application.Run(new MainForm()); 21 } 22 23 public static void handler1(object sender, ThreadExceptionEventArgs e) 24 { 25 MessageBox.Show("哎呀,好像出错了T_T"); 26 Application.Exit(); 27 } 28 29 public static void handler2(object sender, UnhandledExceptionEventArgs e) 30 { 31 MessageBox.Show("哎呀,好像出错了T_T"); 32 Application.Exit(); 33 } 34 }
特别说明:
1>请注意Main方法里各个语句的顺序,集中异常处理的相关代码必须放在Application.Run();语句之前,否则没有效果
2>以上代码都在program.cs中,因为程序入口Main在这里
3>若对这样的处理方式还存在疑问,可以查看官方文档以及例程序源码http://msdn.microsoft.com/zh-cn/library/system.windows.forms.application.setunhandledexceptionmode.aspx
5.最后的建议(请务必看看)
1>集中式异常处理并不是万能的,不建议过分依赖这样的处理方式
2>集中式异常处理只是最后的一道防线,程序内部仍然应该添加尽可能完善的异常处理
3>任何异常处理都应该包括两个部分,即A.记录异常信息(写入ErrorLog或者发送到作者邮箱等等)B.善后操作(退出APP或者其它补救处理)