Is there a difference between "throw" and "throw ex"?

There are some posts that asks what the difference between those two are already.
(why do I have to even mention this...)

But my question is different in a way that I am calling "throw ex" in another error god-like handling method.

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

If try & catch were used in the Main , then I would use throw; to rethrow the error. But in the above simplied code, all exceptions go through HandleException

Does throw ex; has the same effect as calling throw when called inside HandleException ?


Yes, there is a difference;

  • throw ex resets the stack trace (so your errors would appear to originate from HandleException )
  • throw doesn't - the original offender would be preserved.

  • (I posted earlier, and @Marc Gravell has corrected me)

    Here's a demonstration of the difference:

            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
        }
    

    and here is the output:

    Exception 1:
       at UnitTester.Program.DivByZero() in <snip>DevUnitTesterProgram.cs:line 49
       at UnitTester.Program.ThrowException1() in <snip>DevUnitTesterProgram.cs:line 36
       at UnitTester.Program.TestExceptions() in <snip>DevUnitTesterProgram.cs:line 19
    
    Exception 2:
       at UnitTester.Program.ThrowException2() in <snip>DevUnitTesterProgram.cs:line 43
       at UnitTester.Program.TestExceptions() in <snip>DevUnitTesterProgram.cs:line 25
    

    You can see that in Exception 1, the stack trace goes back to the DivByZero() method, whereas in Exception 2 it does not.

    Take note, though, that the line number shown in ThrowException1() and ThrowException2() is the line number of the throw statement, not the line number of the call to DivByZero() , which probably makes sense now that I think about it a bit...

    Output in Release mode

    Exception 1:

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

    Exception 2:

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

    Is it maintains the original stackTrace in debug mode only?


    The other answers are entirely correct, but this answer provides some extra detalis, I think.

    Consider this example:

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

    If you uncomment the throw arithExc; line, your output is:

    Handling a DivideByZeroException.
    Your stack trace:
       at Program.ThrowTest() in c:somepathProgram.cs:line 44
       at Program.Main() in c:somepathProgram.cs:line 9
    
    No inner exception.
    

    Certainly, you have lost information about where that exception happened. If instead you use the throw; line, this is what you get:

    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:somepathProgram.cs:line 58
       at Program.ThrowTest() in c:somepathProgram.cs:line 46
       at Program.Main() in c:somepathProgram.cs:line 9
    
    No inner exception.
    

    This is a lot better, because now you see that it was the Program.Div method that caused you problems. But it's still hard to see if this problem comes from line 35 or line 37 in the try block.

    If you use the third alternative, wrapping in an outer exception, you lose no information:

    Handling a DivideByZeroException.
    Your stack trace:
       at Program.ThrowTest() in c:somepathProgram.cs:line 48
       at Program.Main() in c:somepathProgram.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:somepathProgram.cs:line 58
       at Program.ThrowTest() in c:somepathProgram.cs:line 35
    

    In particular you can see that it's line 35 that leads to the problem. However, this requires people to search the InnerException , and it feels somewhat indirect to use inner exceptions in simple cases.

    In this blog post they preserve the line number (line of the try block) by calling (through reflection) the internal intance method InternalPreserveStackTrace() on the Exception object. But it's not nice to use reflection like that (the .NET Framework might change their internal members some day without warning).

    链接地址: http://www.djcxy.com/p/34932.html

    上一篇: 使用Java代码进行XML验证

    下一篇: “扔”和“扔前”有区别吗?