“扔”和“扔前”有区别吗?

有一些post询问这两者之间的差别是什么。
(为什么我还要提这个…)

但是我的问题不同于我在另一个错误神似的处理方法中称之为“抛出”的方式。

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. } } 

如果在Main中使用try & catch ,那么我会使用throw; 重新抛出错误。 但是在上面简单的代码中,所有exception都经过了HandleException

throw ex;HandleException调用throw时有同样的效果吗?

是的,有一个区别。

  • throw ex重置堆栈跟踪(所以你的错误似乎来自HandleException
  • throw不 – 原始罪犯将被保留。

(我之前发布的,@Marc Gravell纠正了我)

以下是差异的演示:

  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 } 

这里是输出:

 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中,堆栈跟踪返回到DivByZero()方法,而在例外2中则不是。

但是,请注意, ThrowException1()ThrowException2()中显示的行号是throw语句的行号, 而不是调用DivByZero()的行号,这可能是有道理的,现在我想到了它位…

在释放模式下输出

例外1:

 at ConsoleAppBasics.Program.ThrowException1() at ConsoleAppBasics.Program.Main(String[] args) 

例外2:

 at ConsoleAppBasics.Program.ThrowException2() at ConsoleAppBasics.Program.Main(String[] args) 

是否仅在debugging模式下维护原始的堆栈跟踪?

其他答案是完全正确的,但是这个答案提供了一些额外的问题,我想。

考虑这个例子:

 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); } } 

如果你取消注释throw arithExc; 行,你的输出是:

 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. 

当然,你已经失去了有关exception发生的信息。 如果你使用throw; 行,这是你得到的:

 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. 

这样好多了,因为现在你看到了Program.Div方法导致了你的问题。 但是仍然很难看到这个问题是来自try块中的第35行还是第37行。

如果使用第三种替代方法,则在外部exception中进行包装,则不会丢失任何信息:

 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行 。 但是,这需要人们searchInnerException ,在简单情况下使用内部exception感觉有点间接。

在这篇博文中,他们通过调用(通过reflection) Exception对象的internal实例方法InternalPreserveStackTrace()来保留行号(try块的行)。 但是使用这样的reflection并不好(.NET框架有可能会在没有警告的情况下改变它们的internal成员)。

不,这将导致exception具有不同的堆栈跟踪。 只在catch处理程序中使用没有任何exception对象的throw会使堆栈跟踪不变。

你可能想从HandleException返回一个布尔值,不pipe是否重新抛出exception。

当你抛出前,抛出exception成为“原”的一个。 所以以前的堆栈跟踪将不会在那里。

如果你抛出,exception只是下线 ,你会得到完整的堆栈跟踪。

看看这里: http : //blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

投掷

  try{ // do some operation that can fail } catch (Exception ex) { // do some local cleanup throw; } 

它用Exception保留了堆栈信息

这被称为“重新思考”

如果要抛出新的exception,

 throw new ApplicationException("operation failed!"); 

抛出

 try { // do some operation that can fail } catch (Exception ex) { // do some local cleanup throw ex; } 

它不会将exception发送堆栈信息

这被称为“打破堆栈”

如果要抛出新的exception,

 throw new ApplicationException("operation failed!",ex); 

为了让您对此有不同的看法,如果您为客户端提供API并且想要为内部库提供详细的堆栈跟踪信息,则使用throw特别有用。 通过在这里使用throw,我会在File.Delete的System.IO.File库的情况下得到堆栈跟踪。 如果我使用throw ex,那么这些信息将不会传递给我的处理程序。

  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 } } 
 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. 如果所有第1,2和3行都被注释 – 输出 – 内部ex

  2. 如果所有第2行和第3行都被注释掉 – Output – inner ex System.DevideByZeroException:{“尝试除以零”。} ———

  3. 如果所有第一行和第二行都被注释掉 – Output – inner ex System.Exception:由0分开—-

  4. 如果所有第一行和第三行都被注释掉 – Output – inner ex System.DevideByZeroException:{“试图除以零”。} ———在抛出ex的情况下,StackTrace将被重置;