咳,今天给大家带来一个关于C#中的异常处理,很多朋友在C#中的异常处理非常糟糕的,一个优秀的程序应该具备友好性,容错性,以及准确的异常信息收集的能力。很多Coder也想达到这样的指标呢?跟着我来,稳固你的程序吧!
我们准备做一个程序,具备全局的异常捕获及处理能力,类似大多数知名程序那样弹个窗口,发送错误报告,友好的提示。类似腾讯QQ异常,Firefox异常等异常窗口来进行错误报送,如下图所示:
C#也跟其他的OOP语言一样能够处理可预见的异常信息,如网络连接失败,文件读取失败,数组越界等异常。当你的程序遇到了异常的时候CLR会抛出一个异常给你,这样你就得到了一个处理异常的机会,这个异常会一层一层的返回给调用者,最后返回到Main方法的起始点中,但是如果你没有进行处理的话最终会被CLR处理,它将终止程序。
目前为止,很多的程序都是以感觉哪段代码可能异常就把它try起来然后弹个Message的方式进行提示,好一点处理的还会记录日志信息来解决。如果纯粹看简单的错误提示其实是很难找到错误的,尤其是程序越来越大的时候,甚至有时候你都不知道这个错误是哪个模块出现的,是怎么出现的,点击哪个按钮出现的!是不是得去问客户?在我看来极大多数情况是完全没有必要的,我们完全有能力捕获完整的异常。 好了,废话解释完毕,开始构建我们的具备异常处理捕获及处理能力的程序吧!这里以Winform举例,其他的都差不多,如WPF什么的都是可以捕获全局异常的。
首先我建了个Winform项目命名为WinformException用于处理Bug,为了利于以后项目复用 这个项目是单独用于处理Bug的,在该项目中构建了如下窗体。
这个用于对Bug报送的处理,对客户的错误解释界面,你可以根据自己的业务需求进行更改,总而言之把错误完整的保存下来即可。
出现错误不可怕,可怕的是出了错 你却不知道。
窗口的代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace WinformException { public partial class FrmBugReport : Form { #region 全局变量 Exception _bugInfo; #endregion #region 构造函数 /// <summary> /// Bug发送窗口 /// </summary> /// <param name="bugInfo">Bug信息</param> public FrmBugReport(Exception bugInfo) { InitializeComponent(); _bugInfo = bugInfo; this.txtBugInfo.Text = bugInfo.Message; lblErrorCode.Text = Guid.NewGuid().ToString(); } /// <summary> /// Bug发送窗口 /// </summary> /// <param name="bugInfo">Bug信息</param> /// <param name="errorCode">错误号</param> public FrmBugReport(Exception bugInfo, string errorCode) { InitializeComponent(); _bugInfo = bugInfo; this.txtBugInfo.Text = bugInfo.Message; lblErrorCode.Text = errorCode; } #endregion #region 公开静态方法 /// <summary> /// 提示Bug /// </summary> /// <param name="bugInfo">Bug信息</param> /// <param name="errorCode">错误号</param> public static void ShowBug(Exception bugInfo, string errorCode) { new FrmBugReport(bugInfo, errorCode).ShowDialog(); } /// <summary> /// 提示Bug /// </summary> /// <param name="bugInfo">Bug信息</param> public static void ShowBug(Exception bugInfo) { ShowBug(bugInfo, Guid.NewGuid().ToString()); } #endregion private void btnDetailsInfo_Click(object sender, EventArgs e) { MessageBox.Show("异常详细信息:" + _bugInfo.Message + " 跟踪:" + _bugInfo.StackTrace); } } }
这个项目就完成了。
接下来构建我们的测试程序,以及如何捕捉代码。我在解决方案中再建了一个Winform项目命名为WinformTest做测试,如下图所示:
先来看看我们在Program.cs中做了什么手脚吧,这个就是全局捕捉异常的核心代码,Program.cs代码如下:
using System; using System.Collections.Generic; using System.Windows.Forms; namespace WinformTest { static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { //全局异常捕捉 Application.ThreadException += Application_ThreadException; //UI线程异常 AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; //多线程异常 Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FrmMain()); } //UI线程异常 static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { WinformException.FrmBugReport.ShowBug(e.Exception); } //多线程异常 static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { WinformException.FrmBugReport.ShowBug((Exception)e.ExceptionObject); } } }
第一行我注册了UI线程异常处理事件
Application.ThreadException += Application_ThreadException; //UI线程异常
这个用于捕获主线程的错误,也就是UI,大多数异常都会聚集在此,我们在该事件中处理了异常,程序则不会强制退出。
然后第二行注册了其他多线程异常处理事件(除UI之外的线程)
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; //多线程异常
这个用于捕获主线程之外的所有线程的异常,但是无法让程序不被强制退出,当在这里面的代码执行完毕后程序依然会退出!不过幸运的是我们有其他的办法来解决。
然后看看测试窗体FrmMain.cs的代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Threading; using System.Windows.Forms; namespace WinformTest { public partial class FrmMain : Form { public FrmMain() { InitializeComponent(); } //普通异常测试 private void btnTest1_Click(object sender, EventArgs e) { throw new Exception("啊..我这行代码异常了..."); } //多线程异常测试 private void btnTest2_Click(object sender, EventArgs e) { Thread th = new Thread(() => { throw new Exception("啊哦,异常错误。"); }); th.IsBackground = true; th.Start(); } } }
其实就是两个简单的测试而已,点击按钮会弹出如下界面。
我们可以在确定按钮中编写发送到数据库,或者是把bug详细信息存放到txt中。拿到了Exception,由你任意处置吧,我这里把Exception的StackTrace属性展示到了错误详细信息按钮上,不会看StackTrace的,该反省下了。耐心看下就明白了,是很简单的,它对Bug是如何出现的步骤表示的非常清楚。
好了到此为止,Bug处理也就OK了,还算非常简单的,只是可能我长篇大论了。哈..下次尽量简短..现在应该掌握了对异常的合理处理了吧。
该解决方案的源码下载:WinformException
小知识:如何处理多线程中的异常,让程序不会强制退出。 你可以这样,把多线程中的任务全部try起来。
Thread t = new Thread((ThreadStart)delegate { try { throw new Exception("多线程异常"); } catch (Exception error) { MessageBox.Show("线程异常:" + error.Message + Environment.NewLine + error.StackTrace); } }); t.Start();
你还可以这样,把异常抛回主线程,这个比较推荐。
Thread t = new Thread((ThreadStart)delegate { try { throw new Exception("非窗体线程异常"); } catch (Exception ex) { this.BeginInvoke((Action)delegate { throw ex; }); } }); t.Start();
至此,还有什么疑问或者更好的建议欢迎在此评论补充或进行讨论。
感谢。http://www.wxzzz.com/1173.html