当应用程序捕获一个异常时,代表着我们为应用程序引入了一个“具有破坏性的事件“,异常可能会对系统资源或者应用程序的状态有破坏,我们应该避免这种情况发生。
Dave Abrahams对于异常,定义了3种情况:1. 基本保证;2、强保证;3、无异常抛出。
基本保证,确保在应用程序抛出异常后,没有任何资源泄漏,并且所有对象都处于有效状态;强保证,在基本保证的基础上,又添加了“如果有一场抛出,程序状态保留不变”的条件;“无异常抛出”确保一个操作在任何情况下都不会失败,也就是说一个方法永远不会将异常抛出。通常情况下,强异常机制在“从异常中恢复”和“简化异常处理”之间提供了最好的平衡。
在C#中,由于.NET提供了垃圾回收机制,因此它可以默认提供“基本保证”,因此只有以下情况下才会需要手动释放:在拥有一个实现了IDisposable接口的资源时,跑出了一个异常。
“强异常保证”确保如果操作因为异常而中断,那么程序状态保持不变,操作或者完成或者不改变程序状态,没有中间态。
我们可以通过以下步骤来实现强异常保证。
1. 对将要修改的数据做“防御性的复制”。
2. 对这些数据的“防御性复制”进行修改,这些中间操作可能会引发异常。
3. 将临时的副本和原对象进行交换,交换操作不可能抛出任何异常。
我们可以看以下的代码。
1 public void PhysicalMove( string title, decimal newPay )
2 {
3 // Payroll data is a struct:
4 // ctor will throw an exception if fields aren't valid.
5 PayrollData d = new PayrollData( title, newPay,
6 this.payrollData.DateOfHire );
7
8 // if d was constructed properly, swap:
9 this.payrollData = d;
10 }
下面来讨论“无异常抛出保证”,即方法一致运行,不会有异常抛出。在大部分情况下,这样做是不现实的,但是在以下情况下,还是需要使用“无异常抛出保证”:
1. 对象的终结器,如果在这里抛出异常,程序就会就此中断,而不再执行其他非托管资源的清理工作。
2. 对象的Dispose方法,如果在这个方法中抛出异常,那么系统可能会产生两个异常,.NET环境会丢弃第一个异常,并抛出一个新的异常,在程序的任何地方,我们都无法捕获最初的那个异常,因为它被系统“吞掉”了,这会极大的增加错误处理的复杂度。
2. 在委托链中的某个方法,如果抛出了异常,那么委托链中后面的方法就不会再执行。
异常会严重影响一个程序的控制流,在最坏的情况下,任何事情都可能发生,也可能不发生,当抛出异常时,要知道哪些发生了改变或者哪些没有发生改变的唯一方式就是实现“强类型保证”。