7.2.2 使用 try 和 finally 清除异常
如果你更关心清除而不是错误处理, try 和 finally 会获得你的喜欢。它不仅抑制了出错消息,而且所有包含在 finally 块中的代码在异常被引发后仍然会被执行。尽管程序不正常终止,但你还可以为用户获取一条消息,如清单 7.4 所示。
清单 7.4 在finally 语句中处理异常
通过检测该代码,你可能会猜到,即使没有引发异常处理,finally也会被执行。这是真的——在finally中的代码总是会被执行的,不管是否具有异常条件。为了举例说明如何在两种情况下提供一些有意义的信息给用户, 我引进了新变量bAllFine。bAllFine告诉finally 语段,它是否是因为一个异常或者仅是因为计算的顺利完成而被调用。
作为一个习惯了SEH程序员,你可能会想,是否有一个与__leave 语句等价的语句,该语句在C++中很管用。如果你还不了解,在C++中的__leave 语句是用来提前终止 try 语段中的执行代码,并立即跳转到finally 语段 。坏消息, C# 中没有__leave 语句。但是,在清单 7.5 中的代码演示了一个你可以实现的方案。
清单 7.5 从 try语句 跳转到finally 语句
当这个应用程序运行时,输出结果为
try
finally
__leave
一个 goto 语句不能退出 一个finally 语段。甚至把 goto 语句放在 try 语句 段中,还是会立即返回控制到 finally 语段。因此,goto 只是离开了 try 语段并跳转到finally 语段。直到 finally 中的代码完成运行后,才能到达__leave 标签。按这种方式,你可以模仿在SEH中使用的的__leave 语句。
顺便地,你可能怀疑goto 语句被忽略了,因为它是try 语句中的最后一条语句,并且控制自动地转移到了 finally 。为了证明不是这样,试把goto 语句放到Console.WriteLine 方法调用之前。尽管由于不可到达代码你得到了编译器的警告,但是你将看到goto语句实际上被执行了,且没有为 try 字符串产生的输出。
这个例子的技巧为,它包含了多个catch 语句。第一个捕获了更可能出现的DivideByZeroException异常,而第二个catch语句通过捕获普通异常处理了所有剩下来的异常。
你肯定总是首先捕获特定的异常,接着是普通的异常。如果你不按这个顺序捕获异常,会发生什么事呢?清单7.7中的代码有说明。
清单7.7 顺序不适当的 catch 语句
编译器将捕获到一个小错误,并类似这样报告该错误:
wrongcatch.cs(10,9): error CS0160: A previous catch clause already
catches all exceptions of this or a super type ('System.Exception')
最后,我必须告发CLR异常与SEH相比时的一个缺点(或差别):没有 EXCEPTION_CONTINUE_EXECUTION标识符的等价物,它在SEH异常过滤器中很有用。基本上,EXCEPTION_CONTINUE_EXECUTION 允许你重新执行负责异常的代码片段。在重新执行之前,你有机会更改变量等。我个人特别喜欢的技术为,使用访问违例异常,按需要实施内存分配。