views:

207

answers:

2

Understanding the difference between throw ex and throw, why is the original StackTrace preserved in this example:

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            SomethingThatThrowsException(x);
        }
        catch (Exception)
        {
            throw;
        }
    }

    static void SomethingThatThrowsException(int x)
    {
        int y = x / (x - x);
    }

But not in this one:

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            int y = x / (x - 20);
        }
        catch (Exception)
        {
            throw;
        }
    }

The second scenario is producing the same output as throw ex would?

In both cases, one expects to see the line number where y is initialized.

+2  A: 

Because in the second example, you are rethrowing exception from same method. In first its thrown from different method thats why. In one method scope, stack trace can be only one.

Do as following, the best way is to always wrap an exception inside a new exception, so that you will see exception depth.

"If rethrow has been in issued in the same method (exception stack trace only has one line number information per method, you never see stack trace that in Method A, at line number 2 exception was thrown and then in same Method A, it was rethrown from line number 17, it will only contain last line number from where exception was rethrown"

try        
{            
   int x = 20;            
   int y = x / (x - 20);        
}        
catch (Exception ex)        
{            
   // do something here.. like log or something
   throw new Exception("Internal Exception", ex);        
}
Akash Kava
I understand that this is a work-around that works, but I'm still not understanding why the rethrow in the 2nd example shouldn't have the right line number.
Sam
It is not a workaround. It is the correct way of doing it...
awe
@Akash Kava: Do you have a reference? I'm not convinced you fully understood the OP's question.
280Z28
@awe: I strongly disagree. If you don't have any *useful* information to add in the outer exception, then you should rethrow the exception rather than wrap it.
280Z28
Rethrowing will not preserve line number at all, re throw will show you the line of first stack item to be the line of "rethrow", if your exception is caught and rethrown in same function, you will never know where exception was thrown, and lets take clear example XAML uses it, ASP.NET uses it, I dont think Microsoft guys use internal exception without worrying about performance.
Akash Kava
@280Z: Neither. If you have no useful information to add, then don't catch the exception at all.
John Saunders
@John Saunders: There are a number of reasons to catch exceptions that you can't *recover* from and don't need to wrap, including certain kinds of logging and exception filtering (handling a group of exception types by `catch(Exception ex)` followed by a test that `ex` is one of some number of particular types with common handling for those types.
280Z28
@280Z: Those reasons constitute "useful information to add".
John Saunders
@John Saunders: Now I'm confused. This answer and awe's response suggest that the *preferred* behavior of an exception handler that doesn't recover is to throw a new exception with the previous exception specified as the InnerException instead of using `throw;`. I disagree with that for a number of reasons, including the fact that it changes the exception type making it difficult for a recovering exception handler farther up to catch/handle the actual exception that was thrown.
280Z28
@280Z: In my experience, a handler at a higher level can't make sense of the specific exception in any case; at least, not enough to actually "recover" from it. How would it know what my "FileNotFoundException" actually means?
John Saunders
If you throw a new exception with the original exception as the `innerException` parameter, you will see the line number for the original exception in the exception stack trace.
awe
**@280Z** ; @John Saunders: Exceptions can be filtered at a very early stage by specifying the exception type for the exception object you catch (`catch(FileNotFoundException ex)`), any other exception types will be unhandled. You can even use where statements in the catch to filter on state.
awe
@awe: C# does not support where statements.
John Saunders
+4  A: 

I'm not sure whether this limitation is within the C# language, the CLI, or the Microsoft implementation of these, but your second example is a case where an explicit call to Exception.InternalPreserveStackTrace is required as documented in the following post. Since this method is internal, it generally has to be called through reflection. The performance issues involved in this can be almost completely alleviated by creating an Action<Exception> for the call, as shown at the end of this answer.

Reference: Rethrowing exceptions and preserving the full call stack trace

Edit: After reexamining ECMA-335 Partition I §12.4.2 (Exception handling) and Partition III §4.24 (rethrow), I now believe that the behavior you are seeing is a semantic error in the CLR (Microsoft's implementation of the CLI). The only specific reference to the behavior is "A rethrow does not change the stack trace in the object." In the case described here, the rethrow is in fact altering the stack trace, making the PreserveStackTrace hack a workaround for a know CLR flaw.

static void LongFaultyMethod() 
{ 
    try 
    { 
        int x = 20; 
        int y = x / (x - 20); 
    } 
    catch (Exception ex) 
    { 
        PreserveStackTrace(ex); // <-- add this line
        throw; 
    } 
} 

PreserveStackTrace here is an optimization of the one from that blog entry:

private static readonly Action<Exception> _internalPreserveStackTrace =
    (Action<Exception>)Delegate.CreateDelegate(
        typeof(Action<Exception>),
        typeof(Exception).GetMethod(
            "InternalPreserveStackTrace",
            BindingFlags.Instance | BindingFlags.NonPublic));

public static void PreserveStackTrace(Exception e)
{
    _internalPreserveStackTrace(e);
}
280Z28
This is great, thanks for the info.
Nariman