The difference between re-throwing parameter-less catch and not doing anything?
假设我在两个不同的程序集中有以下两个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //in assembly A public class TypeA { // Constructor omitted public void MethodA { try { //do something } catch { throw; } } } //in assembly B public class TypeB { public void MethodB { try { TypeA a = new TypeA(); a.MethodA(); } catch (Exception e) //Handle exception } } } |
在这种情况下,methoda中的try-catch只提升异常,但不真正处理它。在方法A中使用try-catch有什么好处吗?换句话说,这种类型的try-catch块和根本不使用的块之间有区别吗?
在您的示例中,这样做没有好处。但在某些情况下,只需要鼓吹出一个特定的例外。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void Foo() { try { // Some service adapter code // A call to the service } catch (ServiceBoundaryException) { throw; } catch (Exception ex) { throw new AdapterBoundaryException("some message", ex); } } |
这使您能够轻松地识别异常发生在哪个边界。在这种情况下,您需要确保只针对特定于边界的代码抛出边界异常。
照目前的情况来看,第一种选择似乎很糟糕(或者应该是"无用的"?)想法。然而,很少这样做。异常通常在两种情况下从catch块中重新抛出:
a.您要检查为数据生成的异常,并有条件地将其添加到堆栈中。
1 2 3 4 5 6 7 8 9 10 11 12 | try { //do something } catch (Exception ex) { //Check ex for certain conditions. if (ex.Message ="Something bad") throw ex; else //Handle the exception here itself. } |
B.组件中出现了不可接受的情况,需要将此信息传达给调用代码(通常通过附加一些其他有用的信息或将其包装在另一个异常类型中)。
1 2 3 4 5 6 7 8 9 10 | try { //do something } catch (StackOverflowException ex) { //Bubble up the exception to calling code //by wrapping it up in a custom exception. throw new MyEuphemisticException(ex,"Something not-so-good just happened!"); } |
是的,有区别。当捕获异常时,.NET假定您将以某种方式处理它,堆栈将展开到执行捕获的函数。
如果您不捕获它,它将以未处理的异常结束,这将调用某种诊断(如调试器或异常记录器),那么完整的堆栈及其在实际故障点的状态将可供检查。
因此,如果您捕获了异常,然后重新抛出一个在其他地方没有处理的异常,那么您将剥夺诊断工具关于实际发生的事情的真正有用的信息。
只是重新思考毫无意义——就像你什么都没做一样。
但是,当您实际执行某项操作时,它会很有用——最常见的是记录异常。您还可以更改类的状态,无论什么。
使用您为methoda编写的代码,没有区别。它所要做的就是占用处理器周期。但是,如果有一个您必须释放的资源,那么用这种方式编写代码是有好处的。例如
1 2 3 4 5 6 7 8 | Resource r = GetSomeResource(); try { // Do Something } catch { FreeSomeResource(); throw; } FreeSomeResource(); |
但是这样做没有真正意义。最好使用finally块。
由于这些类位于两个不同的程序集中,您可能只需要捕获用于记录它的异常,然后将其抛出给调用方,以便它能够以它认为合适的方式处理它。一个throw而不是一个throw-ex将保存有关异常产生位置的上下文信息。当您的程序集是一个API/框架时,这可能会被证明是有用的,在这个API/框架中,除非有必要这样做,否则决不应吞咽异常,但如果将异常记录到事件日志中,这仍然有助于解决问题。
程序集a-try catch-block对我没有任何意义。我相信,如果您不打算处理异常,那么为什么要捕获这些异常。它无论如何都会被抛到下一个层次。
但是,如果您正在创建一个中间层API或类似的东西,并且在该层中处理一个异常(因此占用了异常)是没有意义的,那么您可以抛出自己的层applicationexception。当然,重新考虑同样的例外情况是没有意义的。
重新引发异常可用于将其封装为一般异常,如…考虑下面的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class XmlException: Exception{ .... } public class XmlParser{ public void Parse() { try{ .... } catch(IOException ex) { throw new XmlException("IO Error while Parsing", ex ); } } } |
这比对异常进行分类有好处。这就是ASPX文件处理程序和许多其他系统代码如何进行异常封装,从而确定它们到达堆栈的方式及其逻辑流。
当您捕获并抛出时,它允许您在
不要做选项A。正如安东所说,它会吞噬堆栈跟踪。Jaredpar的例子也吞噬了stacktrace。更好的解决方案是:
1 2 3 4 5 6 | SomeType* pValue = GetValue(); try { // Do Something } finally { delete pValue; } |
如果您在c中有需要释放的内容,例如,一个文件流,您有以下两个选择:
1 2 3 4 5 6 7 8 9 10 11 | FileStream stream; try { stream = new FileStream("C:\\afile.txt"); // do something with the stream } finally { // Will always close the stream, even if there are an exception stream.Close(); } |
或者更干净:
1 2 3 4 |
using语句将在完成或关闭异常时释放(并关闭)流。
只有在方法A中可以捕获可以在methodA()中处理的特定异常(例如:日志记录)时,才能使用try catch(ex)block。
另一个选项是使用innerException属性链接异常并将其传递给调用方。这个想法不会杀死堆栈跟踪。