I know most programmers, including me, use Throw exception and Throw in our daily programming and we never see any side effects because of our usage. But best practices always say that we need to know what is good for a particular situation. And often many programmers get confused between the two although they feel they are familiar with them. In fact I also think that.
The basic difference is that the Throw exception overwrites the stack trace and this makes it hard to find the original code line number that has thrown the exception.
Throw basically retains the stack information and adds to the stack information in the exception that it is thrown.
Let us see what it means rather than speaking so many words to better understand the differences. I am using a console application to easily test and see how the usage of the two differ in their functionality.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestingThrowExceptions
{
class Program
{
public void ExceptionMethod()
{
throw new Exception("Original Exception occurred in ExceptionMethod");
}
static void Main(string[] args)
{
Program p = new Program();
try
{
p.ExceptionMethod();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
Now run the code by pressing the F5 key of the keyboard and see what happens. It returns an exception and looks at the stack trace.
System. Exception was unhandled
Message=Original Exception occurred in ExceptionMethod
Source=TestingThrowExceptions
StackTrace:
at TestingThrowExceptions.Program.Main(String[] args) in C:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 26
You find that the exception source is shown as the Main method in the program.cs at line 26.
Now change the code with Throw instead of Throw exception and see what happens:
Message=Original Exception occurred in ExceptionMethod
Source=TestingThrowExceptions
StackTrace:
at TestingThrowExceptions.Program.ExceptionMethod() in C:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 12
at TestingThrowExceptions.Program.Main(String[] args) in C:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 26
Now the stack trace shows that the exception original source is ExceptionMethod() and then it shows the Main method.
If you observe both the stack traces the first one in blue (for Throw Exception) and the second one in Green (for Throw), you will find that the first one has truncated the original stack trace and throwing the exception as if it is a source for the exception.
Basically in MSIL, there are two types of instructions, Throw and rethrow. "Throw exception" is compiled into Throw instruction and "Throw" is compiled into rethrow.
So any time you re-throw the exception using the "Throw" statement it preserves the original stack trace and if you re-throw the exception using "Throw ex" the stack trace is reset to the point where the exception has been thrown.
Now I have modified the code to see what happens if the code has more than one source of exception.
class Program
{
public void AnotherMethod()
{
Program p = new Program();
try
{
p.ExceptionMethod();
}
catch (Exception ex)
{
throw;
}
}
public void ExceptionMethod()
{
throw new Exception("Original Exception occurred in ExceptionMethod");
}
static void Main(string[] args)
{
Program p = new Program();
try
{
p.AnotherMethod();
}
catch (Exception ex)
{
throw;
}
}
}
I have added a method called "AnotherMethod()" which calls the method "ExceptionMethod()" and the method "AnotherMethod()" has been called in the Main method to determine if the original stack trace is preserved and here is the stack trace we got when we ran the program.
Message=Original Exception occurred in ExceptionMethod
Source=TestingThrowExceptions
StackTrace:
at TestingThrowExceptions.Program.ExceptionMethod() in C:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 26
at TestingThrowExceptions.Program.another method() in C:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 20
at TestingThrowExceptions.Program.Main(String[] args) in C:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 40
Observe that it has listed the source in the order of first one is ExceptionMethod and then followed by AnotherMethod and then by the Main method.
So, then the question arises of when shall we use the "Throw ex" in our code? The answer is when you want to throw a meaningful exception to the calling method or code where the exception has been thrown by the source of the exception. Then the second question is how can we ensure what code in our program has raised an exception? The answer is by adding the original exception as the inner exception.
Let us see how by modifying the "Throw" in all of our methods to "Throw exception" and add the original one as an inner exception.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestingThrowExceptions
{
class Program
{
public void AnotherMethod()
{
Program p = new Program();
try
{
p.ExceptionMethod();
}
catch (Exception ex)
{
throw new ApplicationException("operation failed", ex);
}
}
public void ExceptionMethod()
{
throw new Exception("Original Exception occurred in ExceptionMethod");
}
static void Main(string[] args)
{
Program p = new Program();
try
{
p.AnotherMethod();
}
catch (Exception ex)
{
throw new ApplicationException("Could not get data", ex);
}
}
}
}
Let us see the stack trace by running the program
Message=Could not get data
Source=TestingThrowExceptions
StackTrace:
(3) at TestingThrowExceptions.Program.Main(String[] args) inC:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 40
……
InnerException: System.ApplicationException
Message=operation failed
Source=TestingThrowExceptions
StackTrace:
(2) at TestingThrowExceptions.Program.AnotherMethod() inC:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 20
(3) at TestingThrowExceptions.Program.Main(String[] args) inC:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 35
InnerException:
Message=Original Exception occurred in ExceptionMethod
Source=TestingThrowExceptions
StackTrace:
(1) at TestingThrowExceptions.Program.ExceptionMethod() in C:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 26
(2) at TestingThrowExceptions.Program.AnotherMethod() inC:\TestProjects\TestingThrowExceptions\TestingThrowExceptions\Program.cs:line 15
InnerException:
So, when looking at the stack trace, you can still determine where the exception originally occurred.
If your application stores the meaningful application in a database or is to be displayed to the user in a UI screen we should follow this method and the actual stack trace should be logged in a log file for debugging the code.