如何在.NET中从exception堆栈跟踪中隐藏当前方法?
我想知道是否有办法从方法内部抛出exception,但不在exception堆栈跟踪中包含该方法。 例如
void ThrowSomeException() { throw new SomeException(); }
然后,如果我从一个名为Foo()
的方法调用该方法,我希望exception堆栈跟踪从at Foo()
,而不是at ThrowSomeException()
。 我假设如果可能的话,可能是通过在方法上使用属性。
我对一般的答案很感兴趣,但是如果这是不可能的,我真正要做的是为IEnumerable
创建一个扩展方法AssertEqual()
,我将在NUnit测试中使用它。 因此,当我调用myEnumerable.AssertEqual(otherEnumerable)
并且它失败时,NUnit应该在测试方法内报告错误,而不是在扩展方法内。
谢谢!
也许您可以派生自己的exception类型并覆盖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 stackTrace = new List (); 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); } } }
使用本答案末尾的代码,您可以编写如下代码:
[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
这是一个可能的实现:
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
请注意,这只会导致标记方法从堆栈跟踪的一个特定表示中排除,而不是从堆栈跟踪本身中排除。 我知道无法实现后者。
PS:如果您只想在调试会话期间隐藏调用堆栈窗口中的方法,只需将[DebuggerHidden]
属性应用于该方法即可。
我猜你想要这样做是为了合并用于创建exception的代码?
在这种情况下,为什么不写一个GetException()
函数而不是写一个ThrowException()
GetException()
函数? 然后在Foo中,只需throw GetException();
GetStackTraceWithoutHiddenMethods()扩展方法的答案很好,除了Exception.ToString()不使用StackTrace属性,它调用GetStackTrace(),而不是可以覆盖。 因此,如果希望将此扩展方法与其自己的基于exception的类型一起使用,则必须重写ToString()而不是覆盖StackTrace属性。
请注意,这是对现有答案的改进。
这个问题的接受答案非常笨拙,因为
- 它使用纯字符串确定我们需要通过名称隐藏堆栈跟踪的方法。
- 拆分堆栈跟踪基于
string.Split
方法。 - 它隐藏了
StackTrace
属性中的一个方法,不再需要。
但它会覆盖StackTrace
属性本身(问题的所需行为)
最受欢迎的答案非常清晰,因为
- 它使用属性而不是将方法的名称指定为字符串。
- 它可以用于隐藏
StackTrace
多个方法。
但它真的很复杂,只为扩展方法添加了两个类。 其中最重要的弱点是不会覆盖StackTrace
属性本身。
在阅读了前两个解决方案之后,我想我已经达到了最简单,最干净的方式(它结合了这个问题的两个最佳答案中的最佳方案)
这是需要的基础设施。
[AttributeUsage(AttributeTargets.Method, Inherited = false)] public sealed class StackTraceHiddenAttribute : Attribute { } public class SomeException : Exception { public override string StackTrace { get { return string.Concat( new StackTrace(this, true) .GetFrames() .Where(frame => !frame.GetMethod().IsDefined(typeof(StackTraceHiddenAttribute), true)) .Select(frame => new StackTrace(frame).ToString()) .ToArray()); } } }
这是使用以前的基础设施的一个例子
[StackTraceHidden] // apply this to all methods you want to be omitted in stack traces static void Throw() { throw new SomeException(); } static void Foo() { Throw(); } static void Main() { try { Foo(); } catch (Exception e) { Console.WriteLine(e.StackTrace); } }
编辑根据@Stakx对此答案的评论,该答案在放入后立即删除 ,他指出了一些重要的想法:
此解决方案仅适用于自定义定义的exception,并且他的解决方案适用于所有exception类型,这绝对是正确的。
根据这一点,这里有一个扩展方法,没有更复杂,可以解决问题并处理所有exception类型。
public static class ExceptionExtensions { public static string GetStackTraceWithoutHiddenMethods(this Exception e) { return string.Concat( new StackTrace(e, true) .GetFrames() .Where(frame => !frame.GetMethod().IsDefined(typeof(StackTraceHiddenAttribute), true)) .Select(frame => new StackTrace(frame).ToString()) .ToArray()); } }
除了集成IsDefined
方法之外,它几乎与他的代码相同。
如果你告诉编译器积极地内联你的方法,它应该防止你的方法首先进入调用堆栈:
[MethodImpl(MethodImplOptions.AggressiveInlining)] void ThrowSomeException() { throw new SomeException(); }
此属性从.NET 4.5开始提供。
但是,这只被认为是对编译器的强烈暗示 ,并且在某些情况下它仍然不会导致内联。 例如,如果从不同的程序集调用该方法,或者在调试模式下编译,我认为它不能内联它。
解决此问题的一种方法是仅使用助手创建exception,并将其从调用代码中抛出 。
public static InvalidOperationException InvalidOperation(FormattableString message) { return new InvalidOperationException(FormattableString.Invariant(message)); } // calling code throw ExceptionHelpers.InvalidOperation($"{0} is not a valid value", value);
但是如果你的帮助器方法有逻辑来确定是否抛出exception,那可能不适合你:
public static class Require { [ContractAnnotation("condition:false => halt")] [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerHidden] public static void True(bool condition) { if (!condition) { throw ExceptionHelpers.InvalidOperation($"Expected condition was not met."); } } }
在这些情况下,您可能不得不使用exception堆栈跟踪,因为此处的其他答案显示。 例如,您可能希望忽略使用DebuggerHiddenAttribute
标记的方法。
上述就是C#学习教程:如何在.NET中从exception堆栈跟踪中隐藏当前方法?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1012175.html