您的位置:首页 > 其它

异常处理,简单而又复杂的问题

2011-01-16 21:01 330 查看
一个好的软件系统,必定从整体上有一个好的异常处理机制;一个好的程序员必定对异常有着充分的理解和认识。异常处理是程序设计中一个非常重要的方面,也是程序设计中的一大难点。异常处理,是一个简单而又复杂的问题。
1、为什么需要异常?

异常就是可预测但是又没办法消除的一种错误,应用程序必须处理独立于程序本身的这些错误,如:

A、访问不存在的文件

B、请求内存时遇到可用内存不足

C、访问某一资源时没有足够权限

D、用户获取了非法数据(如年份为5000)

在没有异常机制的时候,是通过检测方法返回的[错误码]来处理这些错误的,这种方式有很大的弊端:

A
代码将变得很复杂,因为每调用一个方法都必须跟随若干判断语句(这意味着代码中有很多烦人的if语句),这些测试没有集中在一起,违反了代码一致性的原则,这是一个重要的软件设计原则。

B
程序员必须在程序的设想阶段预见所有可能的情况,最麻烦的是也必须定义程序的反应以及如何处理每一种类型的错误。这显然大大增加了方法之间的耦合性,调用者和被调用者如此紧密的相互依存,可以想象一下在面向对象的程序中这简直是个玩笑。

鉴于错误代码的缺陷,也鉴于面向对象编程的要求,异常处理便应运而生,调用某个方法时只需要catch一个Exception便可以将该方法抛出的所有异常捕获到(因为这些异常肯定都继承自Exception),大大简化了程序的编码。虽然也有多个catch块的情况,但大多数情况下顶层调用程序是不那么关心被调用的方法有多少个异常的。
2何时需要抛出异常?

前面说了很多通过错误代码处理错误情况的弊端,但这种方式并不是一无是处的,因为异常也并非适用于所有情况。遇到错误情况,是选择返回错误代码还是抛出异常,有时候是一件很难拿捏的事情,程序员们常常能很好的理解异常机制并且说的头头是道,但是却常常使用不当。下面从宏观上说明何时需要抛出异常:

抛异常有一个最基本的一般性原则:应用程序的功能若按照正常方式执行就不应当抛出异常!
举例说明:

2-A
方法参数无法通过验证时抛异常

方法始终对传入参数进行有效性验证是一个非常重要的编码原则,传入的参数不合法,该方法应立即停止运行,并抛出异常!因为参数的规则说明是作为方法签名的一部分发布的,正常情况下调用者应该很清楚调用这个方法所需要的参数的规格,传入参数有误,程序未按正常方式执行,抛出异常。感兴趣的读者可以看一下微软的源代码,大部分方法都对参数进行了验证,并在非法情况时抛出了异常。

2-B
越底层越公用性的程序越需要抛出异常

底层程序是为顶层程序提供服务的,从最底层到最顶层很可能会有很多层级,底层程序运行出问题,将导致顶层程序出现异常(当然这并不是绝对的)。虽然异常在顶层或在底层抛出都不会导致系统崩溃,但在异常发生的第一刻将其抛出,才能使程序的错误信息更精确,尽早提示用户操作出错。
3何时捕获异常以及如何处理异常?

方法调用可能会有很深的嵌套层次,对于某个异常,究竟应该在多深的调用层次捕获异
常并合理处理异常?并没有确切的答案,对于每一种类型的异常都需要仔细研究哪个级别更适合纠正引发该问题的条件并恢复运行或者正确的结束应用程序。
对于捕获到的异常,有两种处理方式,要么就地处理,要么继续向外抛出,该选择哪种方式,有一个一般性的原则:如果方法出现异常对后续程序运行有影响则抛出异常,否则不抛。越顶层的程序抛出的概率越低,越底层的程序抛出的概率越高。
举例说明:
Void fun()
{
Try
{
A=fun_A();
B=fun_B();
…….
}
Catch(Exception ex)
{

………
}
}
情况一:如果A语句和B语句是并行关系,并且调用者希望当一条语句出现问题时,并不终止程序的运行(此时可以或者什么都不做,或者提示用户运行过程中出现了问题),那么当fun_A中捕获到异常的时候,它应该就地处理掉该异常,因为A出错不会影响B的执行。
情况二:如果A语句和B语句是前驱后继关系,那么当fun_A中出现异常的时候,他应该将异常抛出,终止调用函数的继续执行,因为后面的程序已经没有执行意义。
情况三:如果fun_A()只会在一个程序点被调用,那么他和后面的语句是并行还是串行就很明确,这时抛不抛异常也很明确。但是如果函数fun_A()有很强的公用性,它不知道会是什么运行关系,则毫不犹豫的抛出异常,由调用者来处理。
此外,对于捕获到的异常,可以将主动权交给用户,提示用户是否继续,如果用户选择继续则不抛,如果用户选择终止,则抛出,程序结束。
4异常的弊端
很多人更倾向于用异常来代替方法返回的错误代码,但是异常也有两个比较明显的弊端,需认真分析,详细统筹,再确定该不该用异常。弊端如下:

A
用异常会使代码变得难读。

B
CLR的异常处理所需要的性能代价远高于简单的检查错误代码。
5总结
前面说了很多原则、规律,但很多都加了“一般性”这三个字,一般性就代表所说的不是绝对的,究竟何处抛出异常,何处捕获异常,实际上最终是由需求决定的。对于一个软件系统,异常的定义、抛出、捕获以及再抛出是需要统筹考虑的,不是想抛就抛,想捕获就捕获,掌握了前面的原则再加上认真的分析实际情况才能对异常处理达到运筹帷幄的地步。异常的抛出有两种情况,分别是首次抛出和捕获后的再抛出,分别对应2
和3的叙述,对于首次抛出的异常,有一个大原则是不变的:应用程序的功能若按照正常方式执行,不抛异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: