NET问答: 如何在当前方法中捕获调用者信息?

咨询区

  • flipdoubt

当我用 C# 记录日志的时候,我如何知道当前方法的调用者是谁?我知道有一个 System.Reflection.MethodBase.GetCurrentMethod() 方法,但我想获取更深一步的 堆栈信息,我考虑过解析调用堆栈,但我希望能找到一个更加干净简洁的方式,类似于用 Assembly.GetCallingAssembly() 捕获方法调用者的程序集。.

回答区

  • Coincoin

如果你使用 C#5 + 的话,推荐使用 caller info, 如下代码所示:

    class Program
    {
        static void Main(string[] args)
        {
            SendError("hello world!");
        }

        public static void SendError(string Message, [CallerMemberName] string callerName = "")
        {
            Console.WriteLine(callerName + "  called me.");
        }
    }

NET问答: 如何在当前方法中捕获调用者信息?

除了调用者,还可以获取调用者的路径 [CallerFilePath] 和 行号 [CallerLineNumber] 。


Tikall

有两种方式可以实现。

  • 特性
  • Stack

下面我比较一下它们的性能差异。

  • 编译时判断调用者
        static void Log(object message, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0)
        {
            // we'll just use a simple Console write for now    
            Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
        }

  • Stack 中判断调用者
        static void Log2(object message)
        {
            // frame 1, true for source info
            StackFrame frame = new StackFrame(1, true);
            var method = frame.GetMethod();
            var fileName = frame.GetFileName();
            var lineNumber = frame.GetFileLineNumber();

            // we'll just use a simple Console write for now    
            Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
        }

** 两者的性能比较 **

Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms

正如你看到的,使用 特性 是相当快的,实际上比 Stack 快将近 25 倍。

点评区

获取当前执行方法的调用者,其实我只知道 Stack 这种方式,有意思的是 Tikall 大佬提到的:特性比 Stack 快 25 倍,乍一看挺夸张的,那我就来简单验证下。

  • 测试环境:.NET5 & x64 & Windows10 & Release
    public class Program
    {
        static void Main(string[] args)
        {
            var mystest = new MyTest();

            var watch = Stopwatch.StartNew();

            for (int i = 0; i < 10000; i++)
            {
                mystest.Log($"hello");
            }

            watch.Stop();

            var watch2 = Stopwatch.StartNew();

            for (int i = 0; i < 10000; i++)
            {
                mystest.Log2($"world");
            }

            watch2.Stop();

            Console.WriteLine($"Log={watch.ElapsedMilliseconds}s, Log2={watch2.ElapsedMilliseconds}s");
        }
    }

    public class MyTest
    {
        public void Log(object message, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0)
        {
            // we'll just use a simple Console write for now    
            Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
        }

        public void Log2(object message)
        {
            // frame 1, true for source info
            StackFrame frame = new StackFrame(1, true);
            var method = frame.GetMethod();
            var fileName = frame.GetFileName();
            var lineNumber = frame.GetFileLineNumber();

            // we'll just use a simple Console write for now    
            Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
        }
    }

NET问答: 如何在当前方法中捕获调用者信息?

从图中可以看出,大概有 5 倍的差距,这也许是 .NET 5 中对 Stack 做了优化吧~~~

原文链接:https://stackoverflow.com/questions/171970/how-can-i-find-the-method-that-called-the-current-method