“扔”和“扔前”有区别吗?
有一些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,2和3行都被注释 – 输出 – 内部ex
-
如果所有第2行和第3行都被注释掉 – Output – inner ex System.DevideByZeroException:{“尝试除以零”。} ———
-
如果所有第一行和第二行都被注释掉 – Output – inner ex System.Exception:由0分开—-
-
如果所有第一行和第三行都被注释掉 – Output – inner ex System.DevideByZeroException:{“试图除以零”。} ———在抛出ex的情况下,StackTrace将被重置;