Geeks With Blogs
Chris Falter .NET Design and Best Practices

Re-throwing an exception is a little more complex that I had realized.  The gotcha, pointed out by helpful reader "Sander", is that when the initial exception is thrown from directly inside the try block, rather than from within a method call inside the try block, you will lose the line number of the faulty line of code, regardless of how you re-throw the exception.  Here's some sample code that demonstrates what I mean:

using System;

namespace
RethrowExTest
{
    
class
Program
    {
        
static void Main(string
[] args)
        {
            
Console.WriteLine("Whoops! Re-throw from procedural code loses the line number of the exception"
);
            
try
            {
                
try
                {
                    
int
zero = 0;
                    
int
i = 1 / zero;
                }
                
catch (Exception
ex)
                {
                    
Console.WriteLine("Inner catch:"
);
                    
Console
.WriteLine(ex.Message);
                    
Console
.WriteLine(ex.StackTrace);
                    
throw
;
                }
            }
            
catch (Exception
ex)
            {
                
Console.WriteLine("Outer catch:"
);
                
Console
.WriteLine(ex.Message);
                
Console
.WriteLine(ex.StackTrace);
            }
            
Console
.WriteLine();
            
Console.WriteLine("However, re-throw from exception-throwing method retains the line number, as expected"
);
            
try
            {
                
try
                {
                    MethodThatThrowsException();
                }
                
catch (Exception
ex)
                {
                    
Console.WriteLine("Inner catch:"
);
                    
Console
.WriteLine(ex.Message);
                    
Console
.WriteLine(ex.StackTrace);
                    
throw
;
                }
            }
            
catch (Exception
ex)
            {
                
Console.WriteLine("Outer catch:"
);
                
Console
.WriteLine(ex.Message);
                
Console
.WriteLine(ex.StackTrace);
            }
            
Console
.WriteLine();
            
Console.WriteLine("New throw from procedural code loses the line number, as expected"
);
            
try
            {
                
try
                {
                    
int
zero = 0;
                    
int
i = 1 / zero;
                }
                
catch (Exception
ex)
                {
                    
Console.WriteLine("Inner catch:"
);
                    
Console
.WriteLine(ex.Message);
                    
Console
.WriteLine(ex.StackTrace);
                    
throw
ex;
                }
            }
            
catch (Exception
ex)
            {
                
Console.WriteLine("Outer catch:"
);
                
Console
.WriteLine(ex.Message);
                
Console
.WriteLine(ex.StackTrace);
            }
            
Console
.WriteLine();
            
Console.WriteLine("New throw from method loses the line number, as expected"
);
            
try
            {
                
try
                {
                    MethodThatThrowsException();
                }
                
catch (Exception
ex)
                {
                    
Console.WriteLine("Inner catch:"
);
                    
Console
.WriteLine(ex.Message);
                    
Console
.WriteLine(ex.StackTrace);
                    
throw
ex;
                }
            }
            
catch (Exception
ex)
            {
                
Console.WriteLine("Outer catch:"
);
                
Console
.WriteLine(ex.Message);
                
Console
.WriteLine(ex.StackTrace);
            }
        }

        
private static void
MethodThatThrowsException()
        {
            
int
zero = 0;
            
int
i = 1 / zero;
        }
    }
}

The output looks just like what we expected:

Whoops! Re-throw from procedural code loses the line number of the exception
Inner catch:
Attempted to divide by zero.
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 15
Outer catch:
Attempted to divide by zero.
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 22

However, re-throw from exception-throwing method retains the line number, as expected
Inner catch:
Attempted to divide by zero.
   at RethrowExTest.Program.MethodThatThrowsException() in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 103
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 37
Outer catch:
Attempted to divide by zero.
   at RethrowExTest.Program.MethodThatThrowsException() in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 103
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 44

New throw from procedural code loses the line number, as expected
Inner catch:
Attempted to divide by zero.
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 60
Outer catch:
Attempted to divide by zero.
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 67

New throw from method loses the line number, as expected
Inner catch:
Attempted to divide by zero.
   at RethrowExTest.Program.MethodThatThrowsException() in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 103
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 82
Outer catch:
Attempted to divide by zero.
   at RethrowExTest.Program.Main(String[] args) in C:\Projects\Tests\RethrowExceptions\RethrowExTest\Program.cs:line 89

How should we design our exception handling, then?  Follow these 2 simple rules:

1. If you can reasonably expect any exceptions to be thrown only by methods called from within the try block, you can just call "throw" instead of "throw ex" and retain the debugging info you need.  This is what I explained in the previous article.

2. If on the other hand there is a substantial probability of the exception being thrown directly from within the try block, you should log the exception's stack trace before you re-throw it.

P.S. I always welcome comments from knowledgeable readers like "Sanders."  Thanks, man!

 

Posted on Wednesday, March 28, 2007 4:37 PM .NET Gotchas , Coding Practices and Design Patterns | Back to top


Comments on this post: More on How To Re-throw an Exception

# re: More on How To Re-throw an Exception
Requesting Gravatar...
Well always better have one global custom error handler that will log exception and so some other stuff like notify user about problem etc..

You can use
throw new Exception("On Some Process Exception Occures",ex);

that will return "inner exception stack trace"
Left by eolexe on Mar 21, 2008 3:51 AM

Your comment:
 (will show your gravatar)


Copyright © Chris Falter | Powered by: GeeksWithBlogs.net