Problem
Some people have already asked what the difference is between the two. (I’m not sure why I feel compelled to bring this up…)
However, my question differs in a way that I refer to as “throw ex” in another error god-like error handling mechanism.
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 was used in the Main, I would rethrow the error with throw;. However, in the simplified code above, all errors are handled by HandleException.
When called inside HandleException, does throw ex; have the same effect as invoking throw?
Asked by dance2die
Solution #1
Yes, there is a distinction.
Answered by Marc Gravell
Solution #2
(I made a mistake earlier, and @Marc Gravell has pointed it out.)
Here’s an example of the distinction:
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 this is the result:
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
The stack trace in Exception 1 goes back to the DivByZero() method, whereas it does not in Exception 2.
Keep in mind that the line number displayed in ThrowException1() and ThrowException2() refers to the throw statement, not the call to DivByZero(), which makes sense now that I think about it…
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 only in debug mode that it keeps the original stackTrace?
Answered by Shaul Behr
Solution #3
The other responses are valid, however I believe this one adds a little more detail.
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 remove the comment from the throw arithExc; line, you’ll get:
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.
You’ve very certainly forgotten where that exception occurred. This is what you get if you use the toss; line instead:
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 much better, because you can now see that the Program.Div method was the source of your troubles. However, it’s still unclear if the issue stems from line 35 or line 37 of the try block.
You lose no information if you choose the third option, wrapping in an outside 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
You can see that line 35 is the one that causes the problem. However, this forces people to seek for the InnerException, and using inner exceptions in basic cases feels a little indirect.
They invoke the internal intance function InternalPreserveStackTrace() on the Exception object (via reflection) to preserve the line number (line of the try block) in this blog article. However, using reflection in this manner is not desirable (the .NET Framework might change their internal members some day without warning).
Answered by Jeppe Stig Nielsen
Solution #4
The stack trace is preserved when you throw. So lets say Source1 throws Error1 , its caught by Source2 and Source2 says throw then Source1 Error + Source2 Error will be available in the stack trace.
The stack trace is not preserved when you throw ex. As a result, all Source1 errors will be wiped out, leaving just Source2 problems to be transmitted to the client.
When reading things isn’t enough, I recommend watching this video demo of Throw vs Throw ex in C# for additional clarity.
Answered by Shivprasad Koirala
Solution #5
When you throw an exception, it becomes the “original” exception. As a result, the prior stack trace will be missing.
If you throw, the exception will just move down the line, giving you the full stack trace.
Answered by GR7
Post is based on https://stackoverflow.com/questions/730250/is-there-a-difference-between-throw-and-throw-ex