我们要站在异常一定可能会发生的角度来编写C#自定义异常处理程序,应对程序有可能发生的错误建立一个良好的C#自定义异常处理策略。
在做C#自定义异常处理的时候,最好能在应用程序所有的入口处(事件处理函数,主函数,线程入口)使用try-catch。但是不要在程序构造函数入口处添加try-catch,因为此处产生异常,它自己并没有能力来处理,因为它还没有构造完毕,只能再向外层抛出异常。
但我们也不能盲目使用异常。而且使用异常,可能会在一定程度上影响到程序的性能(C#中使用异常一般不影响性能)。
- //自定义异常类
- publicclassMyException:ApplicationException
- {
- publicMyException(stringmessage):base(message)
- {
- }
- publicMyException(stringmessage,ExceptioninnerException)
- :base(message,innerException)
- {
- }
- }
全局异常处理、多线程中的异常处理
将全局异常处理函数的委托加入到Application.ThreadException中,实现全局异常处理,但它只能处理主线程中未捕获的异常。在多线程异常处理时,工作线程/辅线程中产生异常,可以把它转给主线程来完成异常处理。如果线程之间不通知,是无法直接捕捉异常的。若没有去处理工作线程/辅线程中产生的异常,该异常将会“消失”掉。
为什么要把异常处理都交给主线程去做呢?举个例子:在WinForm里我们使用多线程来处理界面元素,一旦有异常发生就将异常消息显示出来。那么,是直接在异常发生后就MessageBox,还是将消息交给MainUI来统一显示?试想一下,程序要是复杂点或是有多个界面采用多线程来显示界面元素,那么采用前者,我们就算知道了异常的详细信息,但可能还是很难找到究竟是哪里出了问题。而通过MainUI来显示,情况就要好很多了,尤其是还设计到其他东西的时候(如:多语言环境)。当然,这个例子只是很小的一个方面。下面就来看怎么来实现:
- usingSystem;
- usingSystem.Drawing;
- usingSystem.Collections;
- usingSystem.ComponentModel;
- usingSystem.Windows.Forms;
- usingSystem.Data;
- usingSystem.Threading;
- namespaceThreadApp
- {
- publicclassfrmMain:System.Windows.Forms.Form
- {
- privateSystem.Windows.Forms.ButtonbtRun;
- /**////
- ///必需的设计器变量。
- ///
- privateSystem.ComponentModel.Containercomponents=null;
- publicdelegatevoidWorkerThreadExceptionHandlerDelegate(Exceptione);
- voidWorkerThreadExceptionHandler(Exceptione)
- {
- this.Text="Disposed.";
- MainUIThreadExceptionHandler(this,newSystem.Threading.ThreadExceptionEventArgs(e));
- }
- publicfrmMain()
- {
- InitializeComponent();
- }
- /**////
- ///清理所有正在使用的资源。
- ///
- protectedoverridevoidDispose(booldisposing)
- {
- if(disposing)
- {
- if(components!=null)
- {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
- Windows窗体设计器生成的代码#regionWindows窗体设计器生成的代码
- /**////
- ///设计器支持所需的方法-不要使用代码编辑器修改
- ///此方法的内容。
- ///
- privatevoidInitializeComponent()
- {
- this.btRun=newSystem.Windows.Forms.Button();
- this.SuspendLayout();
- //
- //btRun
- //
- this.btRun.Location=newSystem.Drawing.Point(72,24);
- this.btRun.Name="btRun";
- this.btRun.TabIndex=0;
- this.btRun.Text="Run";
- this.btRun.Click+=newSystem.EventHandler(this.btRun_Click);
- //
- //frmMain
- //
- this.AutoScaleBaseSize=newSystem.Drawing.Size(6,14);
- this.ClientSize=newSystem.Drawing.Size(224,69);
- this.Controls.Add(this.btRun);
- this.Name="frmMain";
- this.Text="ThreadApp";
- this.ResumeLayout(false);
- }
- #endregion
- /**////
- ///应用程序的主入口点。
- ///
- [STAThread]
- staticvoidMain()
- {
- Application.ThreadException+=newThreadExceptionEventHandler(MainUIThreadExceptionHandler);
- Application.Run(newfrmMain());
- }
- publicstaticvoidMainUIThreadExceptionHandler(Exceptione)
- {
- MainUIThreadExceptionHandler(null,newSystem.Threading.ThreadExceptionEventArgs(e));
- }
- publicstaticvoidMainUIThreadExceptionHandler(objectsender,ThreadExceptionEventArgst)
- {
- MessageBox.Show(t.Exception.Message,"Exception",
- MessageBoxButtons.OK,
- MessageBoxIcon.Warning);
- }
- privatevoidThrowException()
- {
- thrownewNotImplementedException();
- }
- privatevoidRun()
- {
- try
- {
- this.Text="Waiting";//[错误]这里在2.0里是编译不通过的。因为它已经违背了我们的原则——不要跨线程操作(当前线程对界面线程的元素进行了操所)
- Thread.Sleep(2000);
- this.Text="ThrowException";
- ThrowException();
- this.Text="Finished";//[错误](同上)
- }
- catch(Exceptione)
- {
- //如果涉及到多线程的互操作时,
- //可以运用BeginInvoke方法来实现多线程间的互访问。
- this.BeginInvoke(
- newWorkerThreadExceptionHandlerDelegate(
- WorkerThreadExceptionHandler),
- newobject[]{e});}
- }
- privatevoidbtRun_Click(objectsender,System.EventArgse)
- {
- ThreadStartts=newThreadStart(Run);
- Threadt=newThread(ts);
- t.Start();
- //thrownewNotSupportedException();
- }
- }
- }
【编辑推荐】