• Rethrowing exceptions and preserving the full call stack trace


    refer:http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx

    http://geekswithblogs.net/sdorman/archive/2007/08/20/Difference-between-quotthrowquot-and-quotthrow-exquot-in-.NET.aspx

    Did you know that depending on the way you rethrow exceptions you may lose important information? There are already several blog posts that explain and demonstrate the difference between throw and throw ex. I'm realizing only now that none of the two solutions yields the complete call stack trace information!

    Let's see what the problem is and I'll show you the real solution.

    I'll use the following method to generate an exception:

    private static void BadWork()
    {
      int i = 0;
      int j = 12 / i; // Line 10: DivideByZeroException
      int k = j + 1;
    }

    Let's consider what happens if we call BadWork and rethrow the exception with throw ex as follows:

    try
    {
      BadWork();
    }
    catch (Exception ex)
    {
      // do something
      // ...
      throw ex; // Line 24
    }

    Here is the call stack trace that we get in this case:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.WithThrowEx() in Program.cs:line 24
       at Program.Main(String[] args) in Program.cs:line 88

    Line 24 is where throw ex is, not where the exception was thrown.

    Let's now replace throw ex by throw:

    try
    {
      BadWork();
    }
    catch
    {
      // do something
      // ...
      throw; // Line 38
    }


    This time, here is the call stack trace:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.BadWork() in Program.cs:line 10
       at Program.WithThrow() in Program.cs:line 38
       at Program.Main(String[] args) in Program.cs:line 89

    As you can see, we get one additional stack frame this time. Line 10 is where the exception was thrown, which is important information because this is the only information that identifies where the exception actually happened.

    This shows that it's better to use throw rather than throw ex if you want the full stack trace information to be preserved. However, there are cases where throw is not enough. In the following example, throw does not preserve the full stack trace:

    try
    {
      int i = 0;
      int j = 12 / i; // Line 47
      int k = j + 1;
    }
    catch
    {
      // do something
      // ...
      throw; // Line 54
    }

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.WithThrowIncomplete() in Program.cs:line 54
       at Program.Main(String[] args) in Program.cs:line 106

    This time, you can see that information is lost again. Line 54 is where throw is, not where the exception was thrown.

    To preserve the full call stack information, you need to use the following method:

    private static void PreserveStackTrace(Exception exception)
    {
      MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
        BindingFlags.Instance | BindingFlags.NonPublic);
      preserveStackTrace.Invoke(exception, null);
    }

    This method can be used as follows: 

    try
    {
      int i = 0;
      int j = 12 / i; // Line 78
      int k = j + 1;
    }
    catch (Exception ex)
    {
      // do something
      // ...
      PreserveStackTrace(ex);
      throw; // Line 86
    }

    Here is the new call stack information you get with the above code:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.WithThrowAndStackTracePreservation() in Program.cs:line 78
       at Program.WithThrowAndStackTracePreservation() in Program.cs:line 86
       at Program.Main(String[] args) in Program.cs:line 110

    Here is the call stack information you get with throw ex and a call to PreserveStackTrace:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.BadWork() in Program.cs:line 10
       at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 62
       at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 69
       at Program.Main(String[] args) in Program.cs:line 109

    Here we get the full call stack information. Lines 78 and 10 are where the exceptions were thrown. To my knowledge, this is the only way to get complete call stack information in your logs. Without it, it may be difficult to hunt down some bugs.
    It's worth noting that if you call PreserveStackTrace, then you can use throw or throw ex and you'll equally get the full stack trace information.

    I found this useful trick on Chris Taylor's blog. If you want to use this with .NET 1, you should refer to Chris' post because it seems that the InternalPreserveStackTrace method didn't exist before .NET 2.0.

    The complete source code is attached to this post.

  • 相关阅读:
    java学习:字符串比较“==”与“equals”的差异及与c#的区别
    航空8联货运单的作用详解
    flash:二次贝塞尔曲线应用生成飞机路径示意图
    javascript:双链表插入排序
    javascript:算法笔记
    玩聚RT 加入对饭否的统计
    随手小记:创业瞎聊十点
    Python的win32serviceutil之疑似BUG
    撕书记忆法
    中文锐推榜优化·二
  • 原文地址:https://www.cnblogs.com/Refresh-air/p/3315103.html
Copyright © 2020-2023  润新知