Is there a difference between “throw” and “throw ex”?
有一些帖子问这两者之间已经有什么区别了。
(我为什么还要提到这个…)
但我的问题在某种程度上是不同的,我称之为"throw-ex",在另一个错误中是上帝般的处理方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class Program { public static void Main(string[] args) { try { // something } catch (Exception ex) { HandleException(ex); } } private static void HandleException(Exception ex) { if (ex is ThreadAbortException) { // ignore then, return; } if (ex is ArgumentOutOfRangeException) { // Log then, throw ex; } if (ex is InvalidOperationException) { // Show message then, throw ex; } // and so on. } } |
如果在
当在
是的,有区别;
throw ex 重置堆栈跟踪(因此您的错误似乎来自HandleException )江户记1〔2〕没有——原来的罪犯将被保留。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37static void Main(string[] args)
{
try
{
Method2();
}
catch (Exception ex)
{
Console.Write(ex.StackTrace.ToString());
Console.ReadKey();
}
}
private static void Method2()
{
try
{
Method1();
}
catch (Exception ex)
{
//throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
throw ex;
}
}
private static void Method1()
{
try
{
throw new Exception("Inside Method1");
}
catch (Exception)
{
throw;
}
}
(我之前发过帖子,@marc gravell已经纠正了我)
以下是不同之处的演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | static void Main(string[] args) { try { ThrowException1(); // line 19 } catch (Exception x) { Console.WriteLine("Exception 1:"); Console.WriteLine(x.StackTrace); } try { ThrowException2(); // line 25 } catch (Exception x) { Console.WriteLine("Exception 2:"); Console.WriteLine(x.StackTrace); } } private static void ThrowException1() { try { DivByZero(); // line 34 } catch { throw; // line 36 } } private static void ThrowException2() { try { DivByZero(); // line 41 } catch (Exception ex) { throw ex; // line 43 } } private static void DivByZero() { int x = 0; int y = 1 / x; // line 49 } |
下面是输出:
1 2 3 4 5 6 7 8 | Exception 1: at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49 at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36 at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19 Exception 2: at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43 at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25 |
可以看到,在异常1中,堆栈跟踪返回到
不过,请注意,
例外1:
1 2 | at ConsoleAppBasics.Program.ThrowException1() at ConsoleAppBasics.Program.Main(String[] args) |
例外2:
1 2 | at ConsoleAppBasics.Program.ThrowException2() at ConsoleAppBasics.Program.Main(String[] args) |
它是否仅在调试模式下维护原始stacktrace?
其他答案是完全正确的,但我认为这个答案提供了一些额外的细节。
考虑这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | using System; static class Program { static void Main() { try { ThrowTest(); } catch (Exception e) { Console.WriteLine("Your stack trace:"); Console.WriteLine(e.StackTrace); Console.WriteLine(); if (e.InnerException == null) { Console.WriteLine("No inner exception."); } else { Console.WriteLine("Stack trace of your inner exception:"); Console.WriteLine(e.InnerException.StackTrace); } } } static void ThrowTest() { decimal a = 1m; decimal b = 0m; try { Mult(a, b); // line 34 Div(a, b); // line 35 Mult(b, a); // line 36 Div(b, a); // line 37 } catch (ArithmeticException arithExc) { Console.WriteLine("Handling a {0}.", arithExc.GetType().Name); // uncomment EITHER //throw arithExc; // OR //throw; // OR //throw new Exception("We handled and wrapped your exception", arithExc); } } static void Mult(decimal x, decimal y) { decimal.Multiply(x, y); } static void Div(decimal x, decimal y) { decimal.Divide(x, y); } } |
如果取消对
1 2 3 4 5 6 | Handling a DivideByZeroException. Your stack trace: at Program.ThrowTest() in c:\somepath\Program.cs:line 44 at Program.Main() in c:\somepath\Program.cs:line 9 No inner exception. |
当然,您已经丢失了有关异常发生位置的信息。如果您使用
1 2 3 4 5 6 7 8 9 | Handling a DivideByZeroException. Your stack trace: at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2) at System.Decimal.Divide(Decimal d1, Decimal d2) at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58 at Program.ThrowTest() in c:\somepath\Program.cs:line 46 at Program.Main() in c:\somepath\Program.cs:line 9 No inner exception. |
这样做好多了,因为现在你看到是
如果使用第三种选择(包装外部异常),则不会丢失任何信息:
1 2 3 4 5 6 7 8 9 10 | Handling a DivideByZeroException. Your stack trace: at Program.ThrowTest() in c:\somepath\Program.cs:line 48 at Program.Main() in c:\somepath\Program.cs:line 9 Stack trace of your inner exception: at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2) at System.Decimal.Divide(Decimal d1, Decimal d2) at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58 at Program.ThrowTest() in c:\somepath\Program.cs:line 35 |
特别是你可以看到,正是第35行导致了这个问题。然而,这需要人们搜索
在这篇博文中,他们通过调用(通过反射)
让我们了解一下throw和throw的区别。我听说在许多.NET面试中,都会问这个常见的问题。
为了概述这两个术语,throw和throw-ex都用于了解异常发生的位置。throw-ex重写异常的堆栈跟踪,而不考虑实际被抛出的位置。
让我们用一个例子来理解。
让我们了解第一次投掷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | static void Main(string[] args) { try { M1(); } catch (Exception ex) { Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------"); Console.WriteLine(ex.StackTrace.ToString()); Console.WriteLine(" ---------------- Method Name / Target Site --------------"); Console.WriteLine(ex.TargetSite.ToString()); } Console.ReadKey(); } static void M1() { try { M2(); } catch (Exception ex) { throw; }; } static void M2() { throw new DivideByZeroException(); } |
以上输出如下。
显示实际引发异常的完整层次结构和方法名。为m2->m2。与行号一起
其次…让我们通过throw-ex来理解。只需在m2方法中用throw-ex替换throw-ex。如下所示。
throw-ex代码的输出如下。
您可以看到输出中的差异。throw-ex只是忽略前面的所有层次结构,并使用写入throw-ex的行/方法重置堆栈跟踪。
当您确实抛出ex时,抛出的异常将成为"原始"异常。所以所有以前的堆栈跟踪都不存在。
如果您真的抛出了异常,那么异常就沿着这一行往下走,您将得到完整的堆栈跟踪。
不,这将导致异常具有不同的堆栈跟踪。只有在
您可能希望从handleException返回一个布尔值,无论异常是否应重新引发。
msdn代表:
Once an exception is thrown, part of the information it carries is the
stack trace. The stack trace is a list of the method call hierarchy
that starts with the method that throws the exception and ends with
the method that catches the exception. If an exception is re-thrown by
specifying the exception in the throw statement, the stack trace is
restarted at the current method and the list of method calls between
the original method that threw the exception and the current method is
lost. To keep the original stack trace information with the exception,
use the throw statement without specifying the exception.
请看这里:http://blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html
投掷:
1 2 3 4 5 6 7 8 9 | try { // do some operation that can fail } catch (Exception ex) { // do some local cleanup throw; } |
它保留堆栈信息,但有例外
这被称为"回潮"
如果想抛出新的异常,
1 |
投掷EX:
1 2 3 4 5 6 7 8 9 | try { // do some operation that can fail } catch (Exception ex) { // do some local cleanup throw ex; } |
它不会发送堆栈信息,异常
这被称为"打破堆栈"
如果想抛出新的异常,
1 |
为了给您一个不同的视角,如果您向客户机提供一个API,并且想要为内部库提供详细的堆栈跟踪信息,那么使用throw特别有用。通过在这里使用throw,我将得到在本例中用于file.delete的system.io.file库的堆栈跟踪。如果我使用throw-ex,那么该信息将不会传递给我的处理程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | static void Main(string[] args) { Method1(); } static void Method1() { try { Method2(); } catch (Exception ex) { Console.WriteLine("Exception in Method1"); } } static void Method2() { try { Method3(); } catch (Exception ex) { Console.WriteLine("Exception in Method2"); Console.WriteLine(ex.TargetSite); Console.WriteLine(ex.StackTrace); Console.WriteLine(ex.GetType().ToString()); } } static void Method3() { Method4(); } static void Method4() { try { System.IO.File.Delete(""); } catch (Exception ex) { // Displays entire stack trace into the .NET // or custom library to Method2() where exception handled // If you want to be able to get the most verbose stack trace // into the internals of the library you're calling throw; // throw ex; // Display the stack trace from Method4() to Method2() where exception handled } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | int a = 0; try { int x = 4; int y ; try { y = x / a; } catch (Exception e) { Console.WriteLine("inner ex"); //throw; // Line 1 //throw e; // Line 2 //throw new Exception("devide by 0"); // Line 3 } } catch (Exception ex) { Console.WriteLine(ex); throw ex; } |
如果所有行1、2和3都有注释-输出-内部Ex
如果第2行和第3行都有注释-输出-内部ExSystem.DevideByzeroException:"试图除以零。"------
如果第1行和第2行都有注释-输出-内部ExSystem.Exception:偏差为0----
如果第1行和第3行都有注释-输出-内部ExSystem.DevideByzeroException:"试图除以零。"------
如果出现throw-ex,stacktrace将被重置;