有没有真正的世界理由使用throw ex?

在C#中, throw ex几乎总是错误的,因为它会重置堆栈跟踪。

我只是想知道,这有什么现实世界吗? 我能想到的唯一原因是隐藏封闭库的内部,但这是一个非常薄弱的​​原因。 除此之外,我从未在现实世界中遇到过。

编辑:我的意思是throw ex ,就像抛出完全相同的exception而被捕获但是有一个空的堆栈跟踪,就像完全错误一样。 我知道throw ex必须作为一个语言结构存在,以允许抛出一个不同的exception( throw new DifferentException("ex as innerException", ex) ),并且只是想知道是否有一个现场,其中一个throw ex没有错。

没有正当理由重新抛出exception对象,即’throw ex’。

它是一种语言\编译器function,虽然在实践中没有增加任何价值,但它没有增加任何价值。 这是一种类似的情况,能够编写代码来访问空引用的成员 – 它当然可能但没有价值,例如:

  //Possible but not useful. object ref1 = null; ref1.ToString(); 

能够重新抛出exception对象是不幸的,并且经常被新手和有经验的编码人员误解,直到你在生产中得到一个未处理的exception,并记录了截断的堆栈跟踪。

有一种微弱的感觉,它可以用来故意隐藏堆栈跟踪信息,但抛出一个新的exception将是正确的方法来做到这一点。

我甚至可以说能够重新抛出exception对象(即’throw ex’)是一个设计缺陷 – 这样做太容易做错了。 但是,我怀疑这是因为性能原因而在编译器中进行设计权衡,因为它会产生开销来validation’throw ex’不是’re-throw’。 我敢肯定有很多这样的权衡。

我从未见过它是故意使用的,但这当然没有任何意义。

让我们来看看替代方案:

 catch(Exception ex) { // do stuff (logging) throw; // a) continue ex throw new SomeException("blah", ex); // b) wrap throw new SomeException("blah"); // c) replace throw ex; // d) reset stack-trace } 

c)和d)都在放弃堆栈跟踪,d)唯一的’优势’我能看到的是throw ex; 保留ex的确切类型和可能的额外属性(SQL错误)。

那么有没有想要保留除堆栈跟踪之外的所有exception信息的情况? 不正常,但有些猜测:

  • 在模糊库的边界。 但c)在这里看起来更好。
  • 在调用站点上的一些生成代码,就像在RegEx类中一样。

如果你停下来思考它, throw ex实际上经常使用 – 除了一个未命名的局部变量。 (重新)设计语言将非常困难,因此:

 throw new ArgumentException(); 

是有效的,但是

 var ex = new ArgumentException(); throw ex; 

无效(然后扩展它以跟踪更复杂的变量赋值)。 即throw ex是throw语句的“正常”forms,但在重新抛出已经捕获的exception时通常是错误的。

throw ex只是一个错误或由不知道或忘记throw;人做出的错字 throw;

它可能仅在一种情况下,在其答案中由其他人指出:您不希望将正确的堆栈跟踪发送给调用者的情况,但您仍希望保留先前抛出的exception类型。 在实践中,我很难想象有人会需要它的情况,并且不会使用throw new SomeCustomException(...)

FxCop规则CA2200 RethrowToPreserveStackDetails以相同的方式进行,邀请您使用throw;正确地重新抛出exception throw;

 try { // ... } catch (SomeException ex) { Logger.AddException(ex); throw; } 

以下是重新抛出现有exception对象的两个实际应用程序:

reflection

通过reflection调用代码时,抛出的任何exception都将包含在TargetInvocationException中 。 但是你可能希望/需要处理反映“障碍”之外的exception。 在这种情况下,包装将使exception处理代码复杂化。 相反,您可以解开原始exception并重新抛出。

如果您使用的是.NET 4.5+,则可以使用ExceptionDispatchInfo来保留原始堆栈跟踪:

 MethodInfo someMethod = ... try { someMethod.Invoke(); } catch (TargetInvocationException e) { ExceptionDispatchInfo.Capture(e.InnerException).Throw(); } 

任务API

在.NET Task API中,在执行任务期间抛出的exception包含在AggregateException中 。 当使用async / await时,框架将在封面下执行展开,以便抛出原始exception而不是包装器AggregateException。

除了在自动解包时需要重新抛出现有exception的BCL代码之外,有些情况下您无法使用async / await,因此需要手动解包和重新抛出。 有关此实例,请查看Stephen Clearys优秀AsyncEx项目中AsyncContext实现中TaskExtensions.WaitAndUnwrapException的内部用法。

您可能希望记录exception,记录它然后将其作为’throw ex’抛出到上层。 您可能还希望在此层中进行日志记录,但这可能与您获得exception的层无关,或者您可能不需要该exception的详细信息,因为您已经记录了它。

让我们说你有:

 Public int GetAllCustomers() { try { return data.GetAllCustomers() } catch() { Logger.log("Error calling GetAllCustomers"); } } //data Public static int GetAllCustomers() { try { //db operations... } catch(Exception ex) { Logger.log(ex.Stacktrace); throw ex; } }