现在的位置: 首页 > 综合 > 正文

什么时候要抛出异常?

2013年10月15日 ⁄ 综合 ⁄ 共 1613字 ⁄ 字号 评论关闭

在编写代码的过程中,经常会遇到这样的选择,检查到一个不正常的情况,或者某个操作失败,或者检测到
某个异常,此后该怎么办?是抛出一个异常?还是放回一个表示操作失败的返回值?

前一段在北京和小汤他们谈起这个问题的时候,有了一些粗略的想法。这段时间在 Leo4Net 的开发中,一些
想法逐步清晰起来。

=== abc ===

=== 两种方式的不同 ===

    × 返回值很容易被检测,而捕获异常的代码则相对比较长
    × 抛出以及捕获异常需要更多的系统开销
    × 如果忽略返回值,调用者可以继续,但没有捕获的异常则将终止调用者的执行

=== 应该抛出异常的几种情况 ===
在这些情况下,应该是要抛出异常的:

    === 无法通过参数检查 ===
    
    例如空值、参数取值范围等,因为参数的规则说明是作为方法签名的一部分发布的,调用者应该很清楚
    调用这个方法所需要的参数的规格,但是调用者现在传入的错误的参数,说明调用者本身这时候很可能
    就已经出现问题了。
    
    对参数进行检查本身也是一个编写一个方法的良好习惯,这可以保证方法本身在一个相对比较稳定可知
    道状态下工作,因此方法本身将更简单也更稳定。
    
    总之,拒绝非法的参数,并抛出异常。

    === 将可能导致以后的相关功能出错 ===
    
    如果一个异常或者错误的参数将导致以后更多的异常或者以后的某个功能异常,那么不要延迟,立即抛
    出异常。

    例如有一个属性 Filename 用来保存将被保存的文件的名称,一个的方法 Save 将保存到 Filename 所
    指定的文件名中去,显然在给 Filename 属性赋值时,如果给出一个包含非法文件名字符的字符串,那
    么将导致 Save 方法中会有异常发生,不论 Save 方法是否对 Filename 再进行检查,这时候距离设定
    Filename 的代码可能已经相当远了,因此在设定 Filename 的时候,就应该要进行这种有关的检查,
    如果文件名不合法,立即抛出异常,而不要等到以后再来发现这个问题。
    
    上述对参数进行检查其实很多也是这种情况,特别一些空值参数,将可能导致以后使用该参数的时候发
    生空值异常。
    
    
=== 不应该抛出异常的几种情况 ===

    === 不影响程序继续运行 ===
    
    如果一个错误并不影响程序逻辑的正确性,或者不影响此后代码的继续运行,那么可以不必抛出异常。
    
    一个典型的例子例如,从集合中删除指定键值对应的元素,但指定的键值其实并不在集合中,这显然是
    一个意外情况,但是,就此继续下去,以后的程序应该还可以继续有效的运行,因为即使在正常情况下
    也不过是这个元素不再存在于集合中而已。返回一个整数值表示删除掉的元素的个数,如果没有找到给
    定的元素,则返回0表示没有元素被删除。这个例子的另一种情形是,当前集合元素因为某种原因被锁住
    不能删除元素,那么应该抛出一个异常表示删除失败,而不是返回0表示没有删除元素。
    
别人评论:

非常赞同。
对于这种难以明确界定的问题,一方面有大原则,但有时候还需要视实际情况酌情处理。“将可能导致以后的相关功能出错”,我的理解是这样的:对于一定导致以后出错的情况,决定抛出异常——因为一个出错的软件接下来只能产生负面功效;对于可能出错也可能不错的情况,可以延迟抛出异常,在真正碰到错误时应该有更高层的异常捕捉,甚至平台——因为抛出异常是有代价的,如果在有希望的时候马上带来损失,相信有时候不可取,而如果不抛出异常会带来更大损失,也许就立即抛出了。总之,我觉得软件的稳定性要求不同,选择是有区别的。这也许是程序员的抉择——但最终是需求的抉择。
方法始终对参数有效性进行检测——真的很重要。
很多时候,大家想到了异常,在怎样抛出异常的话题下面,怎么详细地定义可预见的异常、尽量避免异常产生的损失、尽量修复和弥补,是一个稳定健壮软件必须做好的事情。

抱歉!评论已关闭.