我需要能够获取到目前为止已调用的所有方法的列表。不幸的是我无法使用 CallerInformation属性,这样我就只剩下 StackTrace 了。
我知道 StackTrace 的性能不太好,所以我希望有其他替代方案可以做到这一点,并且性能不会那么沉重。我发现这篇文章( https://ayende.com/blog/3879/reducing-the-cost-of-getting-a-stack-trace )有一个如何加速堆栈跟踪的示例,但我不确定如何真正使用它来获取方法名称。这是我到目前为止所拥有的:
void Main()
{
var result = getTheStackTrace.Invoke();
}
private Func<object> getTheStackTrace;
public void FastStack()
{
var stackFrameHelperType = typeof(object).Assembly.GetType("System.Diagnostics.StackFrameHelper");
var GetStackFramesInternal = Type.GetType("System.Diagnostics.StackTrace, mscorlib").GetMethod("GetStackFramesInternal", BindingFlags.Static | BindingFlags.NonPublic);
var method = new DynamicMethod("GetStackTraceFast", typeof(object), new Type[0], typeof(StackTrace), true);
var generator = method.GetILGenerator();
generator.DeclareLocal(stackFrameHelperType);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Ldnull);
generator.Emit(OpCodes.Newobj, stackFrameHelperType.GetConstructor(new[] { typeof(bool), typeof(Thread) }));
generator.Emit(OpCodes.Stloc_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Ldnull);
generator.Emit(OpCodes.Call, GetStackFramesInternal);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ret);
getTheStackTrace = (Func<object>)method.CreateDelegate(typeof(Func<object>));
}
如何从对 getTheStackTrace.Invoke() 的调用中获取方法的名称
我对如何获取信息有点困惑。任何帮助,将不胜感激。谢谢。
最佳答案
在现代版本的.Net(6.0 + Framework 4.7.2)上,我找不到提到的性能问题in the article 。我尝试设置 @SledgeHammer 推荐的反射并执行基准测试。我无法使用 StackTrace 检测到足够慢的性能或者对反射方法进行重大改进,以证明在现代应用程序中使用反射的合理性。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
namespace MyBenchmarks
{
[SimpleJob(RuntimeMoniker.Net472, baseline: true)]
[SimpleJob(RuntimeMoniker.Net60)]
public class StackTraceBenchmark
{
private static Func<int, IEnumerable<MethodBase>> _lambda = Create();
public StackTraceBenchmark()
{
}
[Benchmark]
public void StackTrace() => MethodA(false);
[Benchmark]
public void Reflection() => MethodA(true);
[MethodImpl(MethodImplOptions.NoInlining)]
public void MethodA(bool newMethod)
{
MethodB(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodB(bool newMethod)
{
MethodC(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodC(bool newMethod)
{
MethodD(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodD(bool newMethod)
{
MethodE(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodE(bool newMethod)
{
MethodF(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodF(bool newMethod)
{
MethodG(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodG(bool newMethod)
{
MethodH(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodH(bool newMethod)
{
MethodI(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodI(bool newMethod)
{
MethodJ(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodJ(bool newMethod)
{
MethodK(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodK(bool newMethod)
{
MethodZZZ(newMethod);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void MethodZZZ(bool newMethod)
{
if (newMethod)
{
var result = _lambda(0).Take(5).ToList();
}
else
{
var stack = new StackTrace(false);
var result = Enumerable.Range(0, stack.FrameCount)
.Select(i => stack.GetFrame(i).GetMethod())
.Take(5)
.ToList();
}
}
private static Func<int, IEnumerable<MethodBase>> Create()
{
var stackFrameHelperType = typeof(StackTrace).Assembly.GetType("System.Diagnostics.StackFrameHelper");
var getStackFramesInternal = typeof(StackTrace).GetMethod("GetStackFramesInternal", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
var rangeMethod = typeof(Enumerable).GetMethod("Range");
var stackFrameHelperCount = stackFrameHelperType.GetMethod("GetNumberOfFrames");
var selectMethod = typeof(Enumerable).GetMethods().Where(m => m.Name == "Select").First()
.MakeGenericMethod(typeof(int), typeof(MethodBase));
var getMethodBase = stackFrameHelperType.GetMethod("GetMethodBase");
var skip = Expression.Parameter(typeof(int), "skip");
var stackFrameHelperVar = Expression.Parameter(stackFrameHelperType, "stackFrameHelper");
var iParam = Expression.Parameter(typeof(int), "i");
var selectLambda = Expression.Lambda(Expression.Call(stackFrameHelperVar, getMethodBase, iParam), iParam);
var block = Expression.Block(new[] { stackFrameHelperVar }
, Expression.Assign(stackFrameHelperVar, Expression.New(stackFrameHelperType.GetConstructor(new[] { typeof(Thread) })
, Expression.Constant(Thread.CurrentThread)))
, Expression.Call(getStackFramesInternal, stackFrameHelperVar, skip, Expression.Constant(false), Expression.Constant(null, typeof(Exception)))
, Expression.Call(selectMethod
, Expression.Call(rangeMethod
, skip
, Expression.Subtract(Expression.Call(stackFrameHelperVar, stackFrameHelperCount), skip)
)
, selectLambda
)
);
return Expression.Lambda<Func<int, IEnumerable<MethodBase>>>(block, skip).Compile();
}
}
}
关于C# 更快的堆栈跟踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46312372/