日新阁

取日三省之意,记生活珠玑,每日清新,谓日新阁.

 

异常的一些基础知识

人们利用面向对象的机制描述异常类。在这个模型下,人们一般将异常分为如下几个类型。

·异常 (Exception)
这是所有异常的基类,任何一个异常需要从此异常类继承并构造实例。

·系统异常 (SystemException)
系统异常往往用来描述操作系统发生的异常。例如文件不存在,内存不足等。

·应用程序异常 (ApplicationException)
在 SystemException 的托管(Managed)下,为不同俄进程引发应用程序异常。

·非法操作异常 (InvalidOperationException)
此异常隶属于单个应用程序,当在某一个应用程序中发生非法操作(例如:空指针、Access Validation 等),应用程序可以在它自己的堆中托管这些异常。

·其他异常
诸如被零除、格式转换、正则表达式、对象引用等异常,这些异常通常由编写这些应用程序的主语言自动捕获和处理。

一般来说,异常都是按照如下形式继承和传递的。

  Exception
    -- SystemException
      -- ApplicationException
        -- InvalidOperationException
        -- UnhandledException
        -- IOException
          -- FileNotFoundException

可见, 异常的发生也是由先后顺序的。捕获异常也是一样。异常总是被更下一层的类捕获,例如,如果同时在代码中捕获了 IOException 和 FileNotFoundException,那么可能将执行代码路径 2。

try{
    System.IO.File.CreateText("c:\a.txt");
}
catch(IOException ex1)
{……}   //path1
catch(FileNotFoundException ex2)
{……}   //path2
finally
{
System.GC.Release;
}
这就要求在书写代码时正确的捕获异常

异常的结构。在.NET Framework中,所有的异常派生自System.Exception基类。其下有两个子类:SystemException和ApplicationException。自行编写异常类,不建议直接派生自Exception类,而是应该派生自ApplicationException。

Message:描述当前异常的消息
Source:发生当前异常的方法/应用程序
Stacktrace:和当前异常相关的堆栈追踪

如果可能,为每个可能的Exception定制解决方法。将Exception基类放在最后一个Catch子句是一个好办法。
如果不想让程序被错误所终止,要在适当的地方使用try-catch。如果想让异常处理继承,需要在catch子句中写出一些具体的处理方法,空的catch段相当于给异常放行。暗示,在进行完catch子句后程序将继承执行。除非catch子句中有return、throw、System.Environment.Exit()等。

必须正确排列捕获异常的catch子句,范围小的Exception放在前面。可使用finally段来确保收尾工作。

你无法预防异常,但你能处理它们,以避免它们使你的程序崩溃.

当你的程序遇到一个非正常情况,比如说内存不足,它就会引发(throw/raise)一个异常.此时,当前的过程调用将挂起,.net运行时(CLR)将从下至上搜索过程调用堆栈,以查找相应的异常处理程序.也就是说如果抛出异常的代码正处于某个Try块中,运行时将首先使用本地的Catch块(如果有)来处理异常(它将执行在该位置找到的Catch块代码),否则这个程序段将被终止并将异常的处理权交给其调用函数;如果没有函数处理此异常(即在整个调用堆栈中没能找到适当的catch块),最终运行时将会得到并处理它,并立刻将你的程序终止.
即使代码抛出异常,并在catch块中添加了显式的return语句,finally块中的代码仍会被执行.Finally块中的代码将在异常处理代码之后,控制返回到调用过程之前执行.使用break,continue,return语句退出finally块都是非法的。 

Exception类是基本的.NET错误类。会存在一些具体的异常类,但是它们并不都来自Exception类。例如,处理具体的SQL错误。SqlClient类包括了SqlException对象以处理专门针对它的错误。可以加入另一个Catch块来处理针对SQL的错误

创建自己的异常:

㈠声明一个异常,格式如下:class ExceptionName:Exception{}

㈡引发自己的异常:throw(ExceptionName);

要避免使用了try-catch但没有处理异常的情况,否则就相当于给异常放行(这种情况还不如根本就不去捕获它)。
 处理完异常,我们还应该注意在finally块中释放相关资源、还原相关设置信息等收尾工作。
在做异常处理的时候,最好能在应用程序所有的入口处(事件处理函数,主函数,线程入口)使用try-catch。 但是不要在程序构造函数入口处添加try-catch,因为此处产生异常,它自己并没有能力来处理,因为它还没有构造完毕,只能再向外层抛出异常。

对于一些简单的、能够提前避免的错误,我们还是应该在try块外面及早做出处理。
if(z == 0)
{
Console.WriteLine("除数不能为零");
// ...
}
try
{
int x = y/z;
}
catch
{
// ...
}

将全局异常处理函数的委托加入到 Application.ThreadException 中,实现全局异常处理,但它只能处理主线程中未捕获的异常。在多线程异常处理时,工作线程/辅线程中产生异常,可以把它转给主线程来完成异常处理。如果线程之间不通知,是无法直接捕捉异常的。若没有去处理工作线程/辅线程中产生的异常,该异常将会“消失”掉。

为什么要把异常处理都交给主线程去做呢?举个例子:在WinForm里我们使用多线程来处理界面元素,一旦有异常发生就将异常消息显示出来。那么,是直接在异常发生后就MessageBox,还是将消息交给MainUI来统一显示?试想一下,程序要是复杂点或是有多个界面采用多线程来显示界面元素,那么采用前者,我们就算知道了异常的详细信息,但可能还是很难找到究竟是哪里出了问题。而通过MainUI来显示,情况就要好很多了,尤其是还设计到其他东西的时候(如:多语言环境)。当然,这个例子只是很小的一个方面。


posted on 2007-03-19 17:37  flashicp  阅读(389)  评论(0编辑  收藏  举报

导航