为什么 catch 块中的“投掷”和“投掷 ex”的行为方式相同
本文关键字:投掷 方式相 ex 为什么 catch | 更新日期: 2023-09-27 17:57:31
我读到,当在 catch 块中时,我可以使用 "throw;" 或 "throw ex;" 重新抛出当前异常。
寄件人: http://msdn.microsoft.com/en-us/library/ms182363%28VS.80%29.aspx
"要保留具有异常的原始堆栈跟踪信息,请使用 throw 语句而不指定异常。"
但是当我尝试这个
try{
try{
try{
throw new Exception("test"); // 13
}catch (Exception ex1){
Console.WriteLine(ex1.ToString());
throw; // 16
}
}catch (Exception ex2){
Console.WriteLine(ex2.ToString()); // expected same stack trace
throw ex2; // 20
}
}catch (Exception ex3){
Console.WriteLine(ex3.ToString());
}
我得到三个不同的堆栈。我期望第一条和第二条痕迹是相同的。我做错了什么?(还是理解错了?
系统异常:测试 at ConsoleApplication1.Program.Main(String[] args( in c:''Program.cs:line 13系统异常:测试 at ConsoleApplication1.Program.Main(String[] args( in c:''Program.cs:line 16系统异常:测试 at ConsoleApplication1.Program.Main(String[] args( in c:''Program.cs:line 20
throw
只会保留堆栈帧,前提是您不将其从当前帧中丢弃。您可以通过一种方法完成所有这些操作来做到这一点。
看到这个答案:https://stackoverflow.com/a/5154318/1517578
PS:+1 提出一个实际上是一个有效问题的问题。
败了我的回答,但您只需从另一个函数中抛出原始异常即可看到预期的行为:
static void Main(string[] args)
{
try
{
try
{
try
{
Foo();
}
catch (Exception ex1)
{
Console.WriteLine(ex1.ToString());
throw;
}
}
catch (Exception ex2)
{
Console.WriteLine(ex2.ToString()); // expected same stack trace
throw ex2;
}
}
catch (Exception ex3)
{
Console.WriteLine(ex3.ToString());
}
}
static void Foo()
{
throw new Exception("Test2");
}
好吧,我在这个问题上挖掘了更多内容,这是我个人的结论:
永远不要使用"throw;",但总是用 指定原因。
这是我的推理:
我受到以前使用 Java 的经验的影响,并期望 C# 抛出与 Java 非常相似。好吧,我在这个问题上挖掘了更多内容,以下是我的观察:
static void Main(string[] args){
try {
try {
throw new Exception("test"); // 13
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw ex;// 17
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
收益 率:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
直观地说,Java 程序员会期望这两个异常是相同的:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
但是 C# 文档清楚地表明这是可以预期的:
"如果通过在 throw 语句中指定异常来重新引发异常,则会在当前方法处重新启动堆栈跟踪,并且引发异常的原始方法与当前方法之间的方法调用列表将丢失。若要保留具有异常的原始堆栈跟踪信息,请使用 throw 语句而不指定异常。
现在,如果我稍微更改了测试(替换 throw ex; 通过投掷;在第 17 行(。
try {
try {
throw new Exception("test"); // 13
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw;// 17
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
收益 率:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
显然这不是我所期望的(因为这是最初的问题(。我在第二个堆栈跟踪中丢失了原始投掷点。西蒙·怀特黑德(Simon Whitehead(链接了解释,即投掷;仅当当前方法中未发生异常时,才保留堆栈跟踪。因此,在同一方法中没有参数的"抛出"是毫无用处的,因为一般来说,它不会帮助您找到异常的原因。
做任何 Java 程序员都会做的事情,我将第 17 行的语句替换为:
throw new Exception("rethrow", ex);// 17
收益 率:
System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: rethrow ---> System.Exception: test
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
--- End of inner exception stack trace ---
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17
这是一个更好的结果。
然后,我开始使用方法调用进行测试。
private static void throwIt() {
throw new Exception("Test"); // 10
}
private static void rethrow(){
try{
throwIt(); // 15
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw; // 18
}
}
static void Main(string[] args){
try{
rethrow(); // 24
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
同样,堆栈跟踪不是我(Java程序员(所期望的。在 Java 中,两个堆栈是相同的,并且三个方法深度为:
java.lang.Exception: Test
at com.example.Test.throwIt(Test.java:10)
at com.example.Test.rethrow(Test.java:15)
at com.example.Test.main(Test.java:24)
第一个堆栈跟踪只有两个方法深度。
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 15
这就像堆栈跟踪作为堆栈展开过程的一部分进行填充一样。如果我要记录堆栈跟踪以调查此时的异常,我可能会丢失重要信息。
第二个堆栈跟踪是三个方法深度,但第 18 行(抛出;(出现在其中。
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 18
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 24
此观察结果与前面的观察结果类似:当前方法范围不会保留堆栈跟踪,并且我再次丢失了发生异常的调用方法。例如,如果 rethow 写成:
private static void rethrow(){
try{
if (test)
throwIt(); // 15
else
throwIt(); // 17
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
throw; // 20
}
}
收益 率
System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow() in Program.cs:line 20
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26
哪个调用抛出 throwIt(( 抛出了异常?堆栈说第 20 行,那么它是第 15 行还是第 17 行?
解决方案与上一个相同:将原因包装在一个新的异常中,该异常产生:
System.Exception: rethrow ---> System.Exception: Test
at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 17
--- End of inner exception stack trace ---
at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 20
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26
我对这一切的简单结论是永远不要使用"throw;",而是总是使用指定的原因重新抛出一个新的异常。