Is there a difference between “throw” and “throw ex”?

前端 未结 10 2378
夕颜
夕颜 2020-11-22 01:08

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

10条回答
  •  花落未央
    2020-11-22 01:50

    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:\somepath\Program.cs:line 44
       at Program.Main() in c:\somepath\Program.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:\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.
    

    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:\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
    

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

提交回复
热议问题