Is there a difference between "throw" and "throw ex"?
Solution 1
Yes, there is a difference.
-
throw ex
resets the stack trace (so your errors would appear to originate fromHandleException
) -
throw
doesn't - the original offender would be preserved.static void Main(string[] args) { try { Method2(); } catch (Exception ex) { Console.Write(ex.StackTrace.ToString()); Console.ReadKey(); } } private static void Method2() { try { Method1(); } catch (Exception ex) { //throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main) throw ex; } } private static void Method1() { try { throw new Exception("Inside Method1"); } catch (Exception) { throw; } }
Solution 2
(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>\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
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?
Solution 3
Throw preserves the stack trace. 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.
Throw ex does not preserve the stack trace. So all errors of Source1 will be wiped out and only Source2 error will sent to the client.
Sometimes just reading things are not clear , would suggest to watch this video demo to get more clarity , Throw vs Throw ex in C#.
Solution 4
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).
Solution 5
let's understand the difference between throw and throw ex. I heard that in many .net interviews this common asked is being asked.
Just to give an overview of these two terms, throw and throw ex are both used to understand where the exception has occurred. Throw ex rewrites the stack trace of exception irrespective where actually has been thrown.
Let's understand with an example.
Let's understand first Throw.
static void Main(string[] args) {
try {
M1();
} catch (Exception ex) {
Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
Console.WriteLine(ex.StackTrace.ToString());
Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
Console.WriteLine(ex.TargetSite.ToString());
}
Console.ReadKey();
}
static void M1() {
try {
M2();
} catch (Exception ex) {
throw;
};
}
static void M2() {
throw new DivideByZeroException();
}
output of the above is below.
shows complete hierarchy and method name where actually the exception has thrown.. it is M2 -> M2. along with line numbers
Secondly.. lets understand by throw ex. Just replace throw with throw ex in M2 method catch block. as below.
output of throw ex code is as below..
You can see the difference in the output.. throw ex just ignores all the previous hierarchy and resets stack trace with line/method where throw ex is written.
dance2die
I like to read, and build(& break?) stuff. Currently helping folks out on r/reactjs & DEV#react. Reach out to me @dance2die & check out my blog on sung.codes
Updated on August 13, 2021Comments
-
dance2die over 2 years
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 theMain
, then I would usethrow;
to rethrow the error. But in the above simplied code, all exceptions go throughHandleException
Does
throw ex;
has the same effect as callingthrow
when called insideHandleException
? -
Shaul Behr about 15 yearsThis answer is incorrect! See stackoverflow.com/questions/730300/…
-
Marc Gravell about 15 years@Shaul; no, it isn't. I've given details in a comment to your post.
-
Shaul Behr about 15 years@Marc Gravell - my apologies, you were right. Sorry about the downvote; it's too late for me to undo... :(
-
Brann about 13 years@Marc : It seems that throw preserves the original offender ONLY if the throw isn't in the method in which the initial exception was thrown (see this question : stackoverflow.com/questions/5152265/… )
-
LoganS almost 12 years@Xenan - you aren't the only one - I always get it mixed up too. It just looks strange.
-
Jeppe Stig Nielsen almost 12 yearsIf you modify the object referenced by
ex
, for example by setting theex.HelpLink
property, this is reflected with boththrow;
andthrow ex;
. If, on the other hand, you assign to theex
variable (to make it reference another object), then clearlythrow;
will use the original object, whilethrow ex;
will use the new object. It is also interesting to note that even if you use the preferredthrow;
variant, you lose information about the line number inside thetry
block. I just wrote a new answer about that (see also the answer of @Shaul ). -
Marc Gravell almost 12 years@Jeppe to be fair the question clearly doesn't show a reassignment to
ex
-
Jeppe Stig Nielsen almost 12 yearsNo, you're right. In that case
throw;
andthrow ex;
throw the same object, but its stack trace is modified in different way, as you explain in your answer. -
Jasmine about 11 years@Xenan: WYSIWYG - Microsoft Visual Studio editor. And even, I have just experimented throw and throw ex, as its understood by all, throw is the better option as it gives the information from the source of exception. I really wonder why you bring up things which is not necessary. What you see in the output (The real message is what we should be worrying about). I do not understand why do you bring up MSIL part and all here or how it makes sense for optimization.
-
Menahem over 7 yearsIts because the compiler's optimization process inlines short methods such as
DevideByZero
, so the stack trace IS the same. maybe you should post this as a question on it`s own -
xr280xr over 4 yearsI've just read that a benefit of exception filters is that you can avoid unwinding the stack because a catch always unwinds the stack even if you just
throw;
the exception. Is that different from what you are talking about here with preserving the stack trace? When I first read this a couple months ago I thought that is what you meant or they were one in the same. -
Marc Gravell over 4 years@xr280xr not what I was talking about, but an interesting question
-
xr280xr over 4 yearsSo it is simply the value of the
StackTrace
property that is not preserved but in both cases [thomaslevesque.com/2015/06/21/exception-filters-in-c-6/](the stack is unwound). Seems like a strange, and unintuitive design choice for theStackTrace
. Thanks for pointing it out. -
ruffin over 4 years@ScottDorman Looks like your link isn't being forwarded correctly after a blog migration. Looks like it now lives here. Edit: Hey, wait, that's your blog! Fix your own links! ;^D
-
Imad over 4 yearswhat could be the use case for using throw ex?
-
Marc Gravell over 4 years@lmad if I ever find one, I'll let you know
-
dance2die over 3 yearsVery nice diagram there :)
-
Vijay Dwivedi almost 3 yearsnice explanation.