Jun 3, 2012

What is the difference between Throw and Throw Exception in C#?

1 comment
While implementing any application, usually we do throw/re-throwing of exceptions from catch block using the following,
1. catch(Exception ex) { throw ex; }
  OR  

2.catch(Exception ex) { throw; }
Now, Let's see how these two are different and what is the use of each. 
Exception handling seems to be a common problem for .NET developers. We all know that you should wrap operations that have the potential for failing in a try/catch block if you are interested in being able to do something about the error that occurred. 

Here I am going to focus on a particular aspect of exception handling, which I tend to call exception bubbling. Exception bubbling means that, even though you are catching the exception and doing something with it, you want that exception to be "bubble" up from your code to the calling code. So, it has a chance to do something with that exception and very useful while tracking any issue in future. 

I'm sure that, most of the exception handling code you've seen looks similar to this
   1: try
   2: {
   3:     // do some operation that can fail
   4: }
   5: catch (Exception ex)
   6: {
   7:     // do cleanup as your need
   8:     throw ex;
   9: }
The above code looks perfectly reasonable and does the job. It properly catch the exception, does some cleanup and then bubbles up the exception. 

Note: In the real environment you should not catch the general exception as show above. You should be catching the specific exceptions(like SecurityException) and only those that you can do something with that.

However, there is another way of handling errors that looks like this
   1: try
   2: {
   3:     // do some operation that can fail
   4: }
   5: catch (Exception ex)
   6: {
   7:     // do cleanup as your need
   8:     throw;
   9: }
There is a lot of difference between these two calls that won't be seen until you are trying to debug the problem. That difference is in the stack trace information that gets sent with the exception.

In the first case, the stack trace is truncated below the method that failed.i.e., when you look at the stack trace, it will look as if the exception originated in your code. This isn't always the case, particularly if you are bubbling up a CLR generated exception (like a SqlException). This is a problem known as "breaking the stack", because you no longer have the full stack trace information. This happens because you are in essence creating a new exception to throw. 

By using "throw" by itself, you can preserve the stack trace information. You can confirm this by looking at the IL generated for these two code blocks. This makes the difference very obvious since in the first example the IL instruction called is "throw" while in the second the instruction is called "rethrow".

Before you go and change all of your code, there are still places where "throw ex" is appropriate. There are times when you want to add information to the exception that was caught and change it into a more meaningful exception. In these instances you actually want a new exception to be thrown.
Again, there are two ways to achieve this. The most common way that I have seen is,
   1: try
   2: {
   3:     // do some operation that can fail
   4: }
   5: catch (Exception ex)
   6: {
   7:     // do local cleanup as your need
   8:     throw new ApplicationException("Some meaning full message!");
   9: }
However, this still suffers the problem of breaking the stack. Here you are generating a completely new exception and loosing any of the stack trace information from the original exception. Here, what you really want to do is,
   1: try
   2: {
   3:     // do some operation that can fail
   4: }
   5: catch (Exception ex)
   6: {
   7:     // do local cleanup as your need
   8:     throw new ApplicationException("Some meaning full message!", ex);
   9: }
By passing the original exception to the ApplicationException you are preserving the original exception, and it is stack trace information as the inner exception to your ApplicationException.

Note: No need to use "ApplicationException" to rethrow error. Here it is used just for an example. You can use any custom exception, which can be created by inheriting from Exception class.

Conclusion:
  1. Catch only exceptions if they are important to you and you need to do some sort of cleanup as a result.
  2. If you need to bubble-up an exception, use "throw" by itself.
  3. If you want to add additional information to the exception, always pass the original exception as the inner exception.


1 comment :