C++、Java 和 .Net 历来都没有提供任何处理堆栈展开清理代码中出现的异常的好方法。这是不幸的,因为在许多情况下,发生在清理代码中的异常对程序的行为比发生在主行代码中的异常更重要,但对诊断目的帮助不大(特别是因为可能会发生清理异常由于主线异常)。
在 VB.net 中可以编写一个 try/finally 块,这样finally
代码将知道什么异常(如果有)导致代码离开该try
块,而无需声称捕获或处理有问题的异常。不幸的是,这在 C# 中是不可能的,尽管 C# 中的代码可以通过调用用 vb.net 编写的包装器来实现这种效果;即使在 vb.net 中,使用这样的包装器也可能会有所帮助,因为实现正确语义所需的代码有点恶心。如果两种语言中的using
块可以使用类似的接口,则可以改进,如果在堆栈展开期间执行异常IDisposableDuringException { void Dispose(Exception ex);}
,则会调用该接口。Dispose
这将允许代码在Dispose
方法将提供的异常包含在它可能抛出的任何异常中,并且还会根据是否抛出异常而产生处置行为的可能性(例如,如果事务范围通过异常退出,则应该清楚地执行回滚——这就是毕竟它们的重点——但要求它们在通过正常方式退出时隐式执行回滚是 icky)。
关于如果在finally
清理过程中抛出异常会发生什么,我建议无论异常是否挂起,正确的行为应该是抛出类似 custom 的东西CleanupFailureException
。即使调用代码已经准备好捕获例如InvalidOperationException
从主线抛出的内容,主线的成功完成以及随后的InvalidOperationException
清理期间也可能代表与代码准备处理的情况不同的情况,并且应该因此使用不同的异常类型。
顺便说一句,关于代码是否应该假定意外异常是致命的还是非致命的,经常存在一些争论。我认为在许多情况下,正确的方法是让堆栈从意外异常中展开,明确地使正在使用的对象无效,这样所有未来对这些对象的操作都会导致异常。这将产生这样的效果,如果一个意外的异常可能破坏一个至关重要的对象,程序可能会在造成任何损害之前就死掉,但同时允许如果一个异常破坏了一个无论如何都不需要的对象的可能性(例如,如果在从损坏的文件加载文档时发生意外问题,并且在尝试加载文档时与之关联的数据结构被彻底破坏,