18

In C#, throw ex is almost always wrong, as it resets the stack trace.

I just wonder, is there any real world use for this? The only reason I can think of is to hide internals of your closed library, but that's a really weak reason. Apart from that, I've never encountered in the real world.

Edit: I do mean throw ex, as in throwing the exact same exception that was caught but with an empty stacktrace, as in doing it exactly wrong. I know that throw ex has to exist as a language construct to allow throwing a different exception (throw new DifferentException("ex as innerException", ex)) and was just wondering if there is ever a situration where a throw ex is not wrong.

Michael Stum
  • 167,397
  • 108
  • 388
  • 523
  • Do you actually mean `throw ex;` or just `throw;`? – Andrew Barber Dec 29 '10 at 09:04
  • @Andrew Edited the question. I do mean throw ex, just wondering if there is ever a situation where it is not wrong. – Michael Stum Dec 29 '10 at 09:08
  • I figured that's what you meant, but I thought it might help to clear that up since there seemed to be some confusion about it (assuming you were mistakenly meaning `throw;`, for example) – Andrew Barber Dec 29 '10 at 09:15
  • 1
    I know this is old..but I was just thinking about the same thing this week. It seems that it would be an ideal candidate for a compiler warning at the least! – Mr Moose Nov 13 '15 at 05:43
  • @MrMoose **Damien_The_Unbeliever**'s answer addresses why it isn't. – samis Jun 07 '17 at 21:00

6 Answers6

12

I have never seen it used on purpose, but that of course doesn't mean anything.

Let's look at the alternatives:

catch(Exception ex)
{
    // do stuff (logging)
    throw;                                // a) continue ex
    throw new SomeException("blah", ex);  // b) wrap
    throw new SomeException("blah");      // c) replace
    throw ex;                             // d) reset stack-trace
}

Both c) and d) are dropping the stack-trace, the only 'advantage' of d) that I can see is that throw ex; preserves the exact type and possible extra properties (SQL error) of ex.

So is there ever a case where you want to keep all information of an exception except the stack-trace? Not normally, but some speculation:

  • at the boundary of an obfuscated library. But c) would look like a better option here.
  • at the call site to some generated code, like in the RegEx class.
Henk Holterman
  • 236,989
  • 28
  • 287
  • 464
  • 1
    All I can say is that if you are going to kill the stack trace to protect your library IP, you had better be giving me a pretty damn good exception message. :) – Paul Dec 29 '10 at 10:15
  • 1
    @Paul: Yes, like the OP already said it would be rather lame. – Henk Holterman Dec 29 '10 at 10:18
  • Sometimes you see `throw ex` in exception blocks that use an injected strategy for determining how exceptions should be handled: `catch( Exception ex ) { ExceptionStrategy( ex ); }` ... `void ExceptionStrategy( Exception ex ) { /* determine if you can handle/recover, else throw ex*/ }` ... but this is uncommon. – LBushkin Dec 29 '10 at 21:16
3

If you stop to think about it, throw ex is actually used pretty frequently - except with an unnamed local variable. It would be pretty difficult to (re-)design the language so that:

throw new ArgumentException();

was valid, but

var ex = new ArgumentException();
throw ex;

was invalid (and then extend this for tracing more complex variable assignments). I.e. throw ex is the "normal" form of the throw statement, but it is usually wrong when re-throwing an already caught exception.

Damien_The_Unbeliever
  • 220,246
  • 21
  • 302
  • 402
3

There is no valid reason to re-throw exception objects i.e. 'throw ex'.

It's a language\compiler feature that's possible although doesn't add any value in practice over the negative effect it has. This is a similar situation to being able to write code to access members on null references - it's of course possible but has no value, e.g.:

    //Possible but not useful.
    object ref1 = null;

    ref1.ToString();

Being able to re-throw exception objects is unfortunate and very often misunderstood by rookies and experienced coders alike, that is until you get an unhandled exception in production with a truncated stack trace logged.

There is the tenuous feeling that it could be used to intentionally hide stack trace information, but throwing a new exception would be the correct way to do this.

I would go as far as to say that being able to re-throw exception objects (i.e. 'throw ex') is a design flaw - it's just too easy to do the wrong thing. However, I suspect that this is a design trade-off in the compiler for performance reasons as it would incur an overhead to validate that 'throw ex' was not a 're-throw'. I'm sure there are many such trade-offs.

Tim Lloyd
  • 36,374
  • 9
  • 92
  • 127
  • Not sure if I'd compare this to calling members on `null` references; That gives you a run-time error (avoiding the word 'Exception' on purpose here, to avoid confusion), whereas `throw ex;` used to re-throw an exception will work fine, however dubious it may be to do. – Andrew Barber Dec 29 '10 at 10:56
  • @Andrew The example is there to illustrate that the language\compiler may let you express things which have limited or no value. There are certain language\compiler features which let you do things which are just plain wrong but they have been missed or allowed for other reasons e.g. too expensive to validate. – Tim Lloyd Dec 29 '10 at 10:58
  • @chibacity - I know; but I would say the null reference thing actually is worse than not having value; it actually can crash the program. – Andrew Barber Dec 29 '10 at 11:01
  • @Andrew Yes I agree, but I'm trying to paint a bigger picture here :) So you reckon that having a re-started stack trace is a good thing in production! "The application has crashed, but great we've got a stack trace - oh noes some t**t has "throw ex"'d and we have no stack trace - grrr". – Tim Lloyd Dec 29 '10 at 11:03
  • Hmm... funny; I don't recall saying it's a good thing. But rethrowing an exception via `throw ex;` will not, *itself* cause a program to crash; trying to use members on a null reference can. A better comparison might be to some other ostensibly 'harmless' behavior from some generally ill-advised pattern. – Andrew Barber Dec 29 '10 at 11:07
  • But there _is_ a valid reason for rethrowing exception objects: unwrap and rethrow an inner exception. This need will often arise when working with Tasks, where exceptions thrown during execution are wrapped in AggregateException. – Søren Boisen Dec 30 '15 at 14:57
2

throw ex is just an error or a typo made by people who don't know or forget about throw;.

It can be intended probably in one case only, pointed by other people in their answers: the situation where you don't want the proper stack trace to be sent to the caller, but you still want to keep the type of the exception previously thrown. In practice, I can hardly imagine the case when somebody will need it, and will not use throw new SomeCustomException(...) instead.

FxCop rule CA2200 RethrowToPreserveStackDetails goes in the same way, inviting you to rethrow an exception correctly by using throw;:

try
{
    // ...
}
catch (SomeException ex)
{
    Logger.AddException(ex);
    throw;
}
Community
  • 1
  • 1
Arseni Mourzenko
  • 45,791
  • 28
  • 101
  • 185
  • @fejesjoco: The question was: *is there any real world use for this?*. My answer is in the last paragraph (before the note). – Arseni Mourzenko Dec 29 '10 at 09:02
  • I still don't understand those downvotes for an actually valid answer to the question. If it's just because the *beginning* of my answer does not answer the question, than you should probably read until the end before upvoting/downvoting it. – Arseni Mourzenko Dec 29 '10 at 09:30
  • I agree with you MainMa, I had encountered this situation once where i had to rethrow the exception but also had to log it..and throw preserves the line no (i.e stack trace) of the thrown exception whereas the line no changes with throw ex... – taher chhabrawala Dec 29 '10 at 10:49
  • Explaining how to use throw correctly misses the point of the question. – Tim Lloyd Dec 29 '10 at 11:02
  • @chibacity: I suppose you didn't read the entire answer. In all cases, I moved the last sentence of my answer to the beginning, so it must be clearer now. – Arseni Mourzenko Dec 29 '10 at 11:51
  • @MainMan Of course I read your entire answer - it just wasn't very good before you edited it. You are now largely summarising other people's answers. – Tim Lloyd Dec 29 '10 at 11:54
0

Here are two real-world applications of rethrowing existing exception objects:

Reflection

When calling code through reflection, any exceptions thrown will be wrapped in TargetInvocationException. But you may want/need to handle exceptions past the reflection "barrier". In this case, the wrapping will complicate exception handling code. Instead you can unwrap the original exception and rethrow.

If you are on .NET 4.5+, you can use ExceptionDispatchInfo to preserve the original stack trace:

MethodInfo someMethod = ...
try
{
    someMethod.Invoke();
}
catch (TargetInvocationException e)
{
    ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}

Task API

In the .NET Task API, exceptions thrown during execution of tasks are wrapped in AggregateException. When using async/await, the framework will perform unwrapping under the covers in order to throw the original exception instead of the wrapper AggregateException.

Apart from BCL code needing to rethrow existing exceptions when auto-unwrapping, there are cases where you cannot use async/await and therefore need to manually unwrap and rethrow. For a practical example of this, check out the internal uses of TaskExtensions.WaitAndUnwrapException in the AsyncContext implementation in Stephen Clearys excellent AsyncEx project.

Søren Boisen
  • 1,498
  • 20
  • 36
-2

You may want to log the exception, log it and then throw it as 'throw ex' to the upper layer. You may also want to do logging in this layer too but this may have nothing to to with the layer that you got the exception or you may not need the details of the exception since you already logged it.

Lets say you have:

Public int GetAllCustomers()
{
     try
     {
          return data.GetAllCustomers()
     }
     catch()
     {
         Logger.log("Error calling GetAllCustomers");
     }
}
     //data
Public static int GetAllCustomers()
{
     try
     {
          //db operations...
     }
     catch(Exception ex)
     {
          Logger.log(ex.Stacktrace);
          throw ex;
     }
}
Pabuc
  • 5,430
  • 6
  • 33
  • 51
  • 8
    -1 `throw;` is what you should do there.. Read the question one more time since you are not answering it. – jgauffin Dec 29 '10 at 09:03
  • what are you talking about? Do you have any idea what OP asked and what I just answered? and give me 1 good reason to use throw; in the given example. Do I need the exception details on the upper layer? .. – Pabuc Dec 29 '10 at 09:06
  • 5
    Actually you're right. You did answer the question. Although I would never hide information just because I logged it:) – jgauffin Dec 29 '10 at 09:08
  • next time, think before downvoting. I hope you are not coding like you are downvoting.. You may not be doing it, I would also not do it unless I have to but that doesn't mean that this isn't a case to use throw ex; – Pabuc Dec 29 '10 at 09:12
  • 8
    I think in such cases I would rather throw a different exception or not log it. E.g. when designing a library, I would catch and log at the boundaries to the public world and throw a MyLibraryException instead. I just don't see the real world use of such a scenario, but maybe I'm not seeing the big picture – Michael Stum Dec 29 '10 at 09:13
  • @Michael ~ Agreed, but that is a design pattern you chose to use. – Pabuc Dec 29 '10 at 09:34
  • 5
    I too can come up with worthless usages of `throw ex;`, but that still doesn't really answer the question. I *did* remove my downvote when I answered your comment. But I added it again since you yourself said that you would never do as in the answer. Hence not really answering the question. – jgauffin Dec 29 '10 at 10:11