Preserving original StackTrace/LineNumbers in .NET Exceptions

浪子不回头ぞ 提交于 2020-01-01 04:09:12

问题


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.


回答1:


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);
}



回答2:


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);        
}

I am surprised by so many comments not reading my comment !! I wrote in comment that you should probably log this safely, there are various reasons, if the top level code eats up exception and you don't know which and where exception was thrown, logging helps you to intersect exception !!!

If you don't need to log, then don't catch the exception.



来源:https://stackoverflow.com/questions/1585082/preserving-original-stacktrace-linenumbers-in-net-exceptions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!