抛出 Func 时异常的堆栈跟踪未完成

Exception's Stack trace not complete when throwing in Func

我遇到一个问题,当在通过 func 访问的方法中抛出异常时,堆栈跟踪被截断。考虑以下简单复制:

static void Main(string[] args)
{
    Test();
    Console.ReadLine();
}

public static void Test()
{
    try
    {
        Func<string> func = () => MyFunc();
        func();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception.StackTrace:");
        Console.WriteLine(ex.StackTrace.ToString());
    }
}

public static string MyFunc()
{
    Console.WriteLine("Environment.StackTrace:");
    Console.WriteLine(Environment.StackTrace);
    Console.WriteLine(new String('*', 20));
    throw new Exception("Where is my stack trace?");
}

我希望在 catch 块中写出的堆栈跟踪与在 MyFunc 中写出的堆栈跟踪基本相同,但实际上作为异常堆栈跟踪写出的是:

at FuncStackTraceIssue.Program.MyFunc() in Program.cs:line 36
at FuncStackTraceIssue.Program.<>c.<Test>b__1_0() in Program.cs:line 21
at FuncStackTraceIssue.Program.Test() in Program.cs:line 22

如您所见,它只返回到我调用 func (Test) 的函数。 MyFunc 中堆栈跟踪的写出是:

at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at FuncStackTraceIssue.Program.MyFunc() in Program.cs:line 34
at FuncStackTraceIssue.Program.<>c.<Test>b__1_0() in Program.cs:line 21
at FuncStackTraceIssue.Program.Test() in Program.cs:line 22
at FuncStackTraceIssue.Program.Main(String[] args) in Program.cs:line 13

这导致我出现问题的原因是,在实际代码中,这是从几个不同的地方调用的,并传入变量和其他类似的东西,当它出错时,我会记录异常,包括堆栈跟踪。我希望能够准确地看到导致错误的代码路径,最好不必更改我的登录方式。

所以问题是为什么我没有得到完整的堆栈跟踪,我可以做些什么来让它给我完整的堆栈跟踪?

添加到@Dark Falcon 上面的评论:

这不一定是 Func<> 的问题,但与 StackTrace 属性 的工作方式有关。

来自 MSDN's exception class docs(强调我的):

"In contrast, if the exception is re-thrown by using the statement [example follows]... the full call stack is not preserved, and the example would generate the following output [example follows]... A slightly more cumbersome alternative is to throw a new exception, and to preserve the original exception's call stack information in an inner exception. The caller can then use the new exception's InnerException property to retrieve stack frame and other information about the original exception."

作为记录完整堆栈跟踪的可能解决方法,您可以考虑从 Exception class 继承并添加类似堆栈跟踪器的东西 属性:

public class StackTraceableException : Exception
{
    readonly string stackTrace;

    public StackTraceableException() : base() { }

    public StackTraceableException(string message, string stackTrace) :
        base(message)
    {
        this.stackTrace = stackTrace;
    }

    public string StackTrace { get { return stackTrace; } }
}

...并且,继续使用现有的 catch 块:

catch (StackTraceableException ex)
{
    Console.WriteLine("Exception.StackTrace:");
    Console.WriteLine(ex.StackTrace);
}