在C#中,throw ex
几乎总是错误的,因为它会重置堆栈跟踪。
我只是想知道,这有什么现实世界吗?我能想到的唯一原因是隐藏你的封闭库的内部,但这是一个非常薄弱的原因。除此之外,我在现实世界中从未遇到过。
编辑:我的意思是throw ex
,就像投掷完全相同的异常一样,但是有一个空的堆栈跟踪,就像完全错误一样。我知道throw ex必须作为一种语言结构存在,以允许抛出一个不同的异常(throw new DifferentException("ex as innerException", ex)
),并且只是想知道是否存在throw ex
没有错误的情境。
答案 0 :(得分:12)
我从未见过它是故意使用的,但这当然没有任何意义。
让我们来看看替代方案:
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;
保留{{1}的确切类型和可能的额外属性(SQL错误) 1}}。
那么除了堆栈跟踪之外,您是否希望保留异常的所有信息?不正常,但有些猜测:
答案 1 :(得分:3)
如果你停下来想一想,throw ex
实际上经常被使用 - 除了一个未命名的局部变量。 (重新)设计语言将非常困难:
throw new ArgumentException();
有效,但
var ex = new ArgumentException();
throw ex;
无效(然后扩展它以跟踪更复杂的变量赋值)。即throw ex
是throw语句的“正常”形式,但在重新抛出已经捕获的异常时通常是错误的。
答案 2 :(得分:3)
没有正当理由重新抛出异常对象,即'throw ex'。
这是一种语言\编译器功能,尽管在实践中没有增加任何价值,但却没有增加任何价值。这是一种类似的情况,能够编写代码来访问空引用的成员 - 它当然可能但没有价值,例如:
//Possible but not useful.
object ref1 = null;
ref1.ToString();
能够重新抛出异常对象是不幸的,并且经常被新手和有经验的编码人员误解,直到你在生产中得到一个未处理的异常并记录了截断的堆栈跟踪。
有一种微弱的感觉,它可以用来故意隐藏堆栈跟踪信息,但抛出新的异常将是正确的方法。
我甚至可以说能够重新抛出异常对象(即'throw ex')是一个设计缺陷 - 做错事太容易了。但是,我怀疑这是出于性能原因而在编译器中进行设计权衡,因为它会产生开销来验证'throw ex'不是're-throw'。我确信有很多这样的权衡。
答案 3 :(得分:2)
throw ex
只是由不知道或忘记throw;
的人制作的错误或拼写错误。
它可能仅在一种情况下,在其答案中由其他人指出:您不希望将正确的堆栈跟踪发送给调用者的情况,但您仍然希望保留其类型先前抛出的异常。在实践中,我很难想象有人会需要它,并且不会使用throw new SomeCustomException(...)
。
FxCop规则CA2200 RethrowToPreserveStackDetails
采用相同的方式,使用throw;
正确邀请您rethrow an exception:
try
{
// ...
}
catch (SomeException ex)
{
Logger.AddException(ex);
throw;
}
答案 4 :(得分:0)
以下是重新抛出现有异常对象的两个实际应用程序:
通过反射调用代码时,抛出的任何异常都将包含在TargetInvocationException中。但是你可能想要/需要处理反射“障碍”之外的异常。在这种情况下,包装将使异常处理代码复杂化。相反,您可以解开原始异常并重新抛出。
如果您使用的是.NET 4.5+,则可以使用ExceptionDispatchInfo来保留原始堆栈跟踪:
MethodInfo someMethod = ...
try
{
someMethod.Invoke();
}
catch (TargetInvocationException e)
{
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
在.NET Task API中,执行任务期间抛出的异常包含在AggregateException中。当使用async / await时,框架将执行unwrapping,以便抛出原始异常而不是包装器AggregateException。
除了在自动解包时需要重新抛出现有异常的BCL代码之外,有些情况下您无法使用async / await,因此需要手动解包和重新抛出。有关此实例,请查看Stephen Clearys优秀TaskExtensions.WaitAndUnwrapException项目中AsyncContext实施中AsyncEx的内部用法。
答案 5 :(得分:-2)
您可能希望记录异常,记录它然后将其作为'throw ex'抛出到上层。您可能还想在此图层中进行记录,但这可能与您获得异常的图层无关,或者您可能不需要该异常的详细信息,因为您已经记录了它。
让我们说:
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;
}
}