如何获取调用方法的名称?

5

类A的Log方法能否知道调用它的人是谁?

class A
{
    public void Log(string msg)
    {
        Log.Write("method_name: " + msg);
    }
}

我想知道一个类的名称和一个方法的名称。

5个回答

4
您可以使用 StackTraceStackFrame 类。您可以通过调用 StrackTrace 构造函数来获取整个堆栈跟踪,或者使用带有要跳过的帧数的 StackFrame 构造函数来获取特定的堆栈帧。
需要注意的是,由于内联,它可能不准确(例如方法 A 内联了调用您方法的方法 B-将报告方法 A,而不是 B)。
示例代码:
using System;
using System.Diagnostics;

class Test
{
    static void ShowCaller()
    {
        // Use this code if you want multiple frames
        // StackTrace trace = new StackTrace();
        // StackFrame frame = trace.GetFrame(1);

        // Use this code if you're only interested in one frame
        StackFrame frame = new StackFrame(1);
        Console.WriteLine(frame.GetMethod());
    }

    static void Intermediate()
    {
        ShowCaller();
    }

    static void Main()
    {
        Intermediate();
    }
}

在运行优化版本的构建时,这将打印Void Main() - 在运行调试构建时,或者如果您将更多代码放入Intermediate()方法中,它将打印Void Intermediate()

(如评论中所述,这也会带来性能影响。您应该进行测量以确定在您的特定情况下是否可接受。)


我曾经见过一种故意标记为no-inline的日志记录方法,就是为了这个原因。但是,正如你所说,这会带来很大的开销,所以我不建议采用这种方法。 - Steven Sudit
(我的意思是[MethodImpl(MethodImplOptions.NoInlining)],具体点说。) - Steven Sudit
@Steven:但在这种情况下,记录方法不会是问题所在,而是调用记录方法的方法。 - Jon Skeet
@Jon:抱歉,我表达不清楚。我看到的代码有一个私有的 Log 方法,它有公共方法来记录特定严重性代码,比如 FailWarn 等等。这些公共方法有 NoInlining 属性,以便 Log 可以查看 两个 调用者,无论是在调试还是发布模式下。希望能澄清事情。 - Steven Sudit
@Steve:是的,那很有道理。但如果调用Fail或Warn的代码被内联,仍然会导致问题 :) - Jon Skeet
@Jon:是的,如果Foo调用一个记录了Fail的短Bar,我们可能会得到提到Foo而不是Bar的结果。 - Steven Sudit

2
您可以始终在调用堆栈中向后跳

+1:这确实是最好的方法,但要注意性能影响。我不知道影响有多严重,但你应该考虑是否想在发布版本中运行它,特别是如果你经常调用 Log - Chris Schmich

1

你也可以尝试:

 class staitc A
    {
        public staitc  void Log(string msg)
        {
            Log.Write("method_name: " + msg);
        }
    }


        using System.Reflection;

class TestClass
{
       // in method

        private void TestMethod()
    {
           A.Log(MethodBase.GetCurrentMethod().Name +MethodBase.GetCurrentMethod().DeclaringType.FullName);
    }
}

0

从.NET 4.5开始,您可以通过CallerMemberNameAttribute获取调用者信息,该属性是System.Runtime.CompilerServices命名空间的一部分:

using System.Runtime.CompilerServices;

class A
{
    public void Log(string msg, [CallerMemberName] string memberName = "")
    {
        Log.Write("{0}: {1}", memberName, msg);
    }
}

0

可以 - 通过创建一个新的StackTrace对象,然后使用stackTrace.GetFrame(1).GetMethod().Name - 不过,这可能是一项昂贵的操作,因此请测试并检查它是否会导致您的应用程序不必要的减速。


Log.Write 很可能不便宜,所以我关于昂贵的评论可能过早了... :) - Will A
也许不是。Log.Write 应该尽可能地廉价,因为您希望它可用于非错误日志记录。通常的方法是让它尽快返回,将日志条目放入由单个线程(可能具有较低优先级)写入的队列中。 - Steven Sudit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接