抛出保留堆栈跟踪的异常是最常见的期望行为,而在Java中,这可以通过throw ex;
获得,但必须使用C#throw;
。 (另请注意,许多C#程序员经常错误地使用throw ex;
代替throw;
)。
如果有时必须清除堆栈跟踪(这种情况不太常见),则可能只抛出一个新的异常,如throw new MyException(ex.Message, otherDetails);
。
那么,考虑到上述情况,在C#中使用单独的throw;
语句的优势是什么?
或者,换句话说:为什么C#使用一个特殊的单独语句(throw;
),对于最常用的情况(当用户想要保留堆栈跟踪时),它不太为人所知,并使用对于频率较低的情况(清除堆栈跟踪时)更自然throw ex;
?是否有其他可能的用例我没有涉及?
C#和Java中的代码示例:
// This is the C# design
try
{
//...
}
catch (MyException ex)
{
// rethrow so that stack trace is preserved
ex.AppendData(contextVariable);
throw;
}
catch (PrivateException ex)
{
// throw new exception so stack trace is not preserved
throw new PublicException(ex.Message);
}
// This is the Java design
try
{
//...
}
catch (MyException ex)
{
// rethrow so that stack trace is preserved
ex.AppendData(contextVariable);
throw ex;
// and I can even choose to use something like, where ProcessException(ex) will return the same ex
throw ProcessException(ex, contextVariable);
}
catch (PrivateException ex)
{
// throw new so stack trace is not preserved
throw new PublicException(ex.getMessage());
}
答案 0 :(得分:2)
两个抛出变种的原因
catch (SomeExceptionType ex)
{
frobnicate(ex);
throw;
throw ex;
}
存在是因为每个都有用例。只有第一个对于调试发生异常的代码很有用,但在成熟的库中,几乎可以肯定原因是库的外部(硬件/操作系统环境中的故障,或者消费者传递的错误参数)图书馆)。在这种情况下,库内部的细节对使用该库的程序员没有帮助,实际上可能会引起更多的混淆。
此外,封闭源库,尤其是在SaaS模型 1 (webservice)中远程访问的库,可能不希望公开实现细节,因为这些库可能包含专有知识产权。在这种情况下,throw ex;
更可取。
你说Java throw ex;
保留了现有的堆栈跟踪。如何用这种语言避免信息泄漏?除了堆栈跟踪到新的异常对象之外,必须对所有内容进行深层复制,这在抛出更多派生类型的异常时尤其困难。 C#选择使这两种行为都很容易获得。
(您可能仍希望执行过滤后的深层复制,以防信息泄漏到其他异常属性中,例如InnerException
。但是,如果您愿意,C#可以跳过复制步骤。)
1 可以通过其他方式发现本地库的实现细节。保护那些能够完全控制代码的细节requires a webservice-like execution model。
答案 1 :(得分:1)
throw expression;
语句的语义在C#和Java中是不同的。在Java中,异常堆栈跟踪是在实例化异常对象时捕获的。在C#中,在抛出异常对象时捕获异常堆栈跟踪。
此外,CLI(ECMA-335)提供了一个特殊的字节码指令rethrow
,它与throw
有两个不同的主要方式:
rethrow
抛出的异常与当前正在处理的异常实例相同。rethrow
指令保留原始堆栈跟踪。这些语义差异要求C#提供两种不同的形式来抛出异常,或者删除C#用户在预期的重新抛出操作期间能够保留原始异常堆栈跟踪的能力(不要与{{1混淆)字节码指令,可由rethrow
语法专门访问。