检索调用堆栈上活动方法之前的方法的 MethodInfo?

发布于 2024-12-19 00:23:28 字数 679 浏览 1 评论 0原文

我已经为日志记录库创建了包装器,我想在其中记录抛出异常的外部方法的参数:

public void MethodA(string param1, int param2)
{
  try
  {
    throw new Exception();
  }
  catch(Exception ex)
  {
    Logger.Error(ex);
  }
}

...

public static void Error(Exception ex)
{
...
}

如您所见,我想获取有关错误方法级别的 MethodA 信息。我会非常乐意使用:

ParameterInfo[] pi = new StackFrame(1).GetMethod().GetParameters();

因为它拥有一切,但我刚刚阅读了多篇关于性能不佳的帖子。在我看来,也许这不是最好的方法(另一方面,我们正在讨论异常处理 - 此时它已经不太重要)。但是,我找不到使用反射的传统方法来解决这个问题。也许有人可以启发我?

或者也许您仍然会选择 StackFrame 解决方案? 它是对超性能的要求相当小的 asp.net 应用程序。

编辑1: 也许我不太确定我想写什么。我正在使用 StackFrame 解决方案,即使它可能需要长达 20 毫秒才能获取帧,但我真的很好奇如何通过反射获得类似的结果。

I've created wrapper for logging library inside of which I want to log parameters of the outer method that thrown exception:

public void MethodA(string param1, int param2)
{
  try
  {
    throw new Exception();
  }
  catch(Exception ex)
  {
    Logger.Error(ex);
  }
}

...

public static void Error(Exception ex)
{
...
}

As you can see I would like to get MethodA informations on the Error method level. I would be more than happy just to use:

ParameterInfo[] pi = new StackFrame(1).GetMethod().GetParameters();

because it has everything, but I just read multiple SO posts about poor performance. Seems to me that maybe it's not the best way then (on the other hand, we're talking about exception handling - at this point of time it's already not too important). However, I cannot find traditional approach using Reflection to solve this. Perhaps someone can enlighten me?

Or maybe you would still go with StackFrame solution ?
It's asp.net application with rather small demands for ultra performance.

Edit1:
Maybe I was not so sure what I want to write. I AM going with StackFrame solution even if it can take up to 20 ms to get a frame but I am really curious how to get similiar result with reflection.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

念三年u 2024-12-26 00:23:28

您可能会发现我的这个实用方法很有用,因为它还解决了您可能遇到的另一个问题:当您想从构造函数中获取当前堆栈帧时,事情有点奇怪。

using Collections = MikeNakis.Abstract.Collections;
using Diagnostics = System.Diagnostics;
using Generic = System.Collections.Generic;
using Reflection = System.Reflection;
[...]
    ///<summary>Collect stack frames</summary>
    ///<param name="frames_to_skip">Number of frames to skip (usually 1)</param>
    ///<param name="type">When invoking from within a constructor, specify the type of the containing class so as to
    ///skip any other constructors.</param>
    public static Generic.IList<Diagnostics.StackFrame> CollectStackframes( int frames_to_skip, System.Type type = null )
    {
        var frames = new Diagnostics.StackTrace( frames_to_skip + 1, fNeedFileInfo:true ).GetFrames();
        int i = 0;
        if( type != null )
        {
            for( ;  i < frames.Length;  i++ )
            {
                Reflection.MethodBase method = frames[i].GetMethod();
                if( !method.IsConstructor )
                    break;
                //Does not work: if( method.DeclaringType == type )
                //Does not work: if( Equals( method.DeclaringType, type ) )
                //Does not work: if( Equals( method.DeclaringType.TypeHandle, type.TypeHandle ) )
                if( Equals( method.DeclaringType.GUID, type.GUID ) )
                {
                    i++;
                    break;
                }
            }
        }
        var list_of_frame = new Generic.List<Diagnostics.StackFrame>( frames.Length - i );
        for( ;  i < frames.Length;  i++ )
            list_of_frame.Add( frames[i] );
        return Collections.Util.NewListReadonly( list_of_frame );
    }

(注意:Collections.Util.NewListReadonly() 是我的一个静态实用方法,它创建一个只读列表并返回其 IList 接口。)

我这样做不知道 new Diagnostics.StackTrace() 的性能:我猜它和使用反射一样慢(它可能算作反射),但可能比抛出异常快一点。

不过,我会让你对参数部分感到失望:我没有找到在运行时获取方法参数内容的方法。因此,您希望获得的只是参数的类型和名称,而不是它们的值。如果您找到方法,请告诉我。

You might find this utility method of mine useful, since it also solves another problem which you are likely to come across: things are a bit weird when you want to get the current stack frame from within a constructor.

using Collections = MikeNakis.Abstract.Collections;
using Diagnostics = System.Diagnostics;
using Generic = System.Collections.Generic;
using Reflection = System.Reflection;
[...]
    ///<summary>Collect stack frames</summary>
    ///<param name="frames_to_skip">Number of frames to skip (usually 1)</param>
    ///<param name="type">When invoking from within a constructor, specify the type of the containing class so as to
    ///skip any other constructors.</param>
    public static Generic.IList<Diagnostics.StackFrame> CollectStackframes( int frames_to_skip, System.Type type = null )
    {
        var frames = new Diagnostics.StackTrace( frames_to_skip + 1, fNeedFileInfo:true ).GetFrames();
        int i = 0;
        if( type != null )
        {
            for( ;  i < frames.Length;  i++ )
            {
                Reflection.MethodBase method = frames[i].GetMethod();
                if( !method.IsConstructor )
                    break;
                //Does not work: if( method.DeclaringType == type )
                //Does not work: if( Equals( method.DeclaringType, type ) )
                //Does not work: if( Equals( method.DeclaringType.TypeHandle, type.TypeHandle ) )
                if( Equals( method.DeclaringType.GUID, type.GUID ) )
                {
                    i++;
                    break;
                }
            }
        }
        var list_of_frame = new Generic.List<Diagnostics.StackFrame>( frames.Length - i );
        for( ;  i < frames.Length;  i++ )
            list_of_frame.Add( frames[i] );
        return Collections.Util.NewListReadonly( list_of_frame );
    }

(Note: Collections.Util.NewListReadonly() is a static utility method of mine which creates a read-only list and returns its IList<T> interface.)

I do not know about the performance of new Diagnostics.StackTrace(): I would guess that it is as slow as using reflection, (it probably counts as reflection,) but probably a bit faster than throwing an exception.

I will disappoint you about the parameters part though: I have found no way to obtain the contents of parameters to methods at runtime. So, all that you can hope to get is the types and names of the parameters, but not their values. If you find a way, please let me know.

不必你懂 2024-12-26 00:23:28

这个问题有几个不同的方面:

我想记录外部方法的参数

您可以使用基于您最喜欢的 IoC 容器构建的基于拦截的技术,但老实说,我发现最好的方法通常是将参数传递到异常信息中:

throw new Exception("Something went wrong..." + new{param1, param2});

检索调用堆栈上活动方法之前的方法的 MethodInfo?

据我所知,您只打算在引发异常时使用它来记录错误。在这种情况下,我暂时会坚持使用基于 StackFrame 的解决方案,因为这种情况不太可能经常发生。当 C# 5 发布时,您将能够使用 [CallerMemberName] 属性:

public static void Error(Exception ex, [CallerMemberName] string caller = "") {
    ...
}

明智之举

限制捕获此类异常的位置数量是一个很好的做法。一般来说,最好让异常在链上传递,直到最后一刻,您可以优雅地告诉用户发生了意外情况并在此时记录错误。这为您提供了有关异常本身的信息更丰富的堆栈跟踪。如果您想在途中的某个时刻传递附加信息,只需将异常包装在内部异常中并抛出:

catch(Exception ex)
{
   throw new Exception("Something went wrong..." + new{param1, param2}, ex);
}

There are a few different aspects to this question:

I want to log parameters of the outer method

You can use an interception-based technique built off your favorite IoC container, but I've honestly found the best approach is usually just to pass the parameters into the exception information:

throw new Exception("Something went wrong..." + new{param1, param2});

Retrieve MethodInfo of the method that is before the active one on the calling stack?

From what I can gather, you're only ever planning to use this to log an error when an exception is thrown. In that case, I'd just stick with your StackFrame-based solution for the time being, since it's unlikely to occur very often. When C# 5 comes out, you'll be able to use the [CallerMemberName] attribute:

public static void Error(Exception ex, [CallerMemberName] string caller = "") {
    ...
}

A word to the wise

It's good practice to limit the number of places where you catch exceptions like this. Generally it's best just to let the exception pass itself up the chain until the last possible moment, where you can gracefully tell the user something unexpected happened and log the error at that point. This gives you a much more informative stacktrace on the Exception itself. If you want to pass additional information along at some point along the way, just wrap the exception in an inner exception and throw that:

catch(Exception ex)
{
   throw new Exception("Something went wrong..." + new{param1, param2}, ex);
}
一身骄傲 2024-12-26 00:23:28

或者也许您仍然会选择 StackFrame 解决方案?这是 ASP.NET
对超性能要求相当小的应用程序。

只要您仅在错误条件下使用异常,而不是作为方法的“返回值”,我就会采用这种方法。 (参见异常与返回值)。如果您不抛出大量异常(大多数应用程序也不会抛出异常),那么就没有问题。

我们的团队编写了交易软件,我们对其性能进行严格监控。只要抛出的异常不会发生在关键路径中就可以了。通常,如果引发未处理的异常,则性能无关:用户没有等待响应。或者由于异常,该响应不会到达。

请注意,您可能需要在使用硬编码的新 StackFrame(1) 之前进行索引检查。

Or maybe you would still go with StackFrame solution ? It's asp.net
application with rather small demands for ultra performance.

I would go with this approach so long as you use exceptions only in error conditions, not as a "return value" of a method. (See Exceptions vs. Return Values). If you don't throw a lot of exceptions -- and most applications don't -- then you'll be fine.

Our group writes trading software that we heavily monitor for performance. So long as the thrown exceptions don't occur in a critical path you're fine. Usually, if an unhandled exception is thrown then performance isn't relevant: the user isn't waiting for a response. Or that response won't arrive because of the exception.

Note that you may want to do an index check before using a hard-coded new StackFrame(1).

祁梦 2024-12-26 00:23:28

这些都没有。我会选择使用温莎城堡之类的拦截解决方案。那么就很容易获取到被拦截的方法的参数了。

None of these. I would go with an interception solution using something like Castle Windsor. Then it's very easy to get the parameters to the method that was intercepted.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文