为什么c#有单独的重新抛出语句(throw;)而不是重载throw ex ?
本文关键字:throw ex 重载 单独 语句 新抛出 为什么 | 更新日期: 2023-09-27 18:08:55
抛出异常时,保留堆栈跟踪是最常见的行为,在Java中可以通过throw ex;
获得,但在c#中必须使用throw;
。(请注意,许多c#程序员经常错误地使用throw ex;
而不是throw;
)。
如果有时必须清除堆栈跟踪(这种情况不太常见),则可以抛出一个新的异常,如throw new MyException(ex.Message, otherDetails);
。
throw;
语句的优势是什么呢?或者,换句话说:为什么c#使用一个特殊的单独的语句(throw;
),这是不太为人所知的,对于最常用的情况(当用户想要保留stacktrace),它使用更自然的throw ex;
对于不太常见的情况(当stacktrace被清除时)?还有其他我没有提到的用例吗?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());
}
在
中抛出变量的原因catch (SomeExceptionType ex)
{
frobnicate(ex);
throw;
throw ex;
}
的存在是因为每个都有用例。只有第一种方法对调试发生异常的代码有用,但在成熟的库中,几乎可以肯定原因是库的外部原因(硬件/操作系统环境中的故障,或者库的使用者传递的错误参数)。在这种情况下,库内部的细节对使用库的程序员没有帮助,实际上可能会导致更多的混乱。
此外,闭源库,特别是在SaaS模型1 (webservice)中远程访问的库,可能不想公开实现细节,因为这些细节可能包含专有的知识产权。在这种情况下,throw ex;
更可取。
您说Java throw ex;
保留了现有的堆栈跟踪。如何使用这样的语言来避免信息泄露?必须对除堆栈跟踪之外的所有内容进行深度复制,以指向一个新的异常对象,这在抛出更多派生类型的异常时尤其困难。c#的选择使得这两种行为都很容易获得。
(您可能仍然希望执行过滤深度复制,以防在其他异常属性中泄露信息,例如InnerException
。但是c#允许你跳过复制步骤,如果你选择的话。)
1本地库的实现细节可以通过其他方式发现。保护这些细节需要一个类似web服务的执行模型,在这个模型中,对代码的完全控制被维护。
throw expression;
语句的语义在c#和Java中不同。在Java中,异常堆栈跟踪在异常对象实例化时被捕获。在c#中,异常堆栈跟踪是在抛出异常对象时捕获的。
此外,CLI (ECMA-335)提供了一个特殊的字节码指令rethrow
,它与throw
主要在两个方面不同:
-
rethrow
抛出的异常是当前正在处理的异常实例。
rethrow
指令保留原来的堆栈跟踪。这些语义上的差异要求c#要么提供两种不同的抛出异常的形式,要么取消c#用户能够在预期的重抛出操作期间保留原始异常堆栈跟踪的能力(不要与rethrow
字节码指令混淆,throw;
语法专门使其可访问)。