如何在.NET中从异常堆栈跟踪中隐藏当前方法?

gor*_*igh 32 .net c# exception

我想知道是否有办法从方法内部抛出异常,但是不在异常堆栈跟踪中包含该方法.例如

void ThrowSomeException()
{
    throw new SomeException();
}
Run Code Online (Sandbox Code Playgroud)

然后,如果我从一个名为Foo()I 的方法调用该方法,我希望异常堆栈跟踪开始at Foo(),而不是at ThrowSomeException().我假设如果可能的话,可能是通过在方法上使用属性.

我感兴趣的是最普遍的回答,但如果这是不可能的,我真正要做的是创建一个扩展方法AssertEqual()IEnumerable,我会在NUnit的测试使用.所以当我调用myEnumerable.AssertEqual(otherEnumerable)它失败时,NUnit应该在测试方法内报告错误,而不是在扩展方法内.

谢谢!

sta*_*ica 18

使用本答案末尾的代码,您可以编写如下代码:

[HideFromStackTrace] // apply this to all methods you want omitted in stack traces
static void ThrowIfNull(object arg, string paramName)
{
    if (arg == null) throw new ArgumentNullException(paramName);
}

static void Foo(object something)
{
    ThrowIfNull(something, nameof(something));
    …
}

static void Main()
{
    try
    {
        Foo(null);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.GetStackTraceWithoutHiddenMethods());
    }                  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}                      // gets a stack trace string representation
                       // that excludes all marked methods
Run Code Online (Sandbox Code Playgroud)

这是一个可能的实现:

using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method, Inherited=false)]
public class HideFromStackTraceAttribute : Attribute { }

public static class MethodBaseExtensions
{
    public static bool ShouldHideFromStackTrace(this MethodBase method)
    {
        return method.IsDefined(typeof(HideFromStackTraceAttribute), true);
    }
}

public static class ExceptionExtensions
{
    public static string GetStackTraceWithoutHiddenMethods(this Exception e)
    {
        return string.Concat(
            new StackTrace(e, true)
                .GetFrames()
                .Where(frame => !frame.GetMethod().ShouldHideFromStackTrace())
                .Select(frame => new StackTrace(frame).ToString())
                .ToArray());  // ^^^^^^^^^^^^^^^     ^
    }                         // required because you want the usual stack trace
}                             // formatting; StackFrame.ToString() formats differently
Run Code Online (Sandbox Code Playgroud)

请注意,这只会导致标记方法从堆栈跟踪的一个特定表示中排除,而不是从堆栈跟踪本身中排除.我知道无法实现后者.

PS:如果您只想在调试会话期间隐藏调用堆栈窗口中的方法,只需将该[DebuggerHidden]属性应用于该方法即可.


小智 18

现在从 .NET 6 开始使用 StackTraceHiddenAttribute 进行内置

https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stacktracehiddenattribute?view=net-6.0

[StackTraceHidden]
void ThrowSomeException()
{
    throw new SomeException();
}
Run Code Online (Sandbox Code Playgroud)


Pao*_*sco 12

也许您可以派生自己的异常类型并覆盖StackTrace属性getter以排除您的方法:

using System;
using System.Collections.Generic;

class MyException : Exception {

    string _excludeFromStackTrace;

    public MyException(string excludeFromStackTrace) {
        _excludeFromStackTrace = excludeFromStackTrace;
    }

    public override string StackTrace {
        get {
            List<string> stackTrace = new List<string>();
            stackTrace.AddRange(base.StackTrace.Split(new string[] {Environment.NewLine},StringSplitOptions.None));
            stackTrace.RemoveAll(x => x.Contains(_excludeFromStackTrace));
            return string.Join(Environment.NewLine, stackTrace.ToArray());
        }
    }
}

class Program {

    static void TestExc() {
        throw new MyException("Program.TestExc");
    }

    static void foo() {
        TestExc();
    }

    static void Main(params string[] args) {
        try{
            foo();
        } catch (Exception exc){
            Console.WriteLine(exc.StackTrace);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)


Ken*_*nic 5

我猜你想要这样做是为了合并用于创建异常的代码?
在那种情况下,ThrowException()为什么不编写GetException()函数而不是编写函数?然后在Foo,就这么做throw GetException();

  • 在这个特定的情况下并不完全正确,但另外好的想法,谢谢 (2认同)