StackTrace.GetFrame在Release版本中引发NullReferenceException,但在Debug版本中起作用

时间:2019-09-09 19:35:27

标签: c# stack-trace system.reflection

我写了一个类,旨在提供可靠的日志记录消息。我要包括的功能之一是包括用于生成日志消息的调用所源自的类和方法。

我遇到的问题是StackTrace.GetFrame在调试版本和发行版本之间的行为不一致。据我所知,当我在调试中从VS执行程序时,它的行为符合预期。当我执行发布版本交付时,StackTrace.GetFrame会引发NullReferenceException。

这是我的代码的简化示例:

using Logging;

namespace MyApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            Logger Log = new Log(Level.INFO);
            Log.Info(@"Hello, world!");
        }
    }
}

namespace Logging
{
    class Logger
    {
        //Assume a constructor

        public void Info(string message)
        {
            //We will need to go 3 method calls back from StandardEntry()
            //to get the name of the method which called Log.Info
            StandardEntry(text, Level.INFO, 3);
        }

        //This method is called by Logger.Info, Logger.Error, etc
        private void StandardEntry(string message, Level entryLevel, int frameCount)
        {
            if (entryLevel >= this.level)
            {
               string message = $"[{CallingMethod(frameCount)}] {<entryLevel.ToString()>} {message}\r\n";
               File.AppendText(this.LogPath, message);
            }
        }

        private string CallingMethod()
        {
            string retVal;
            StackTrace st = new StackTrace();
            StackFrame sf = st.GetFrame(frameCount);
            MethodBase callingMethod = sf.GetMethod();
            return $"{callingMethod.ReflectedType.Name}.{callingMethod.Name}";
        }
    }
}

我尝试使用为frameCount传递的值,但是较小的数字将返回仍在Logger类内的方法调用。我需要实际调用Log.Info的方法

运行发布的可交付结果时,Program.Main中的Log.Info()会引发NullReferenceException,这使我相信它正在尝试访问超出调用的可执行文件范围的堆栈框架索引。

当我在Visual Studio中调试时,没有异常,CallingMethod()返回期望的表达式。

2 个答案:

答案 0 :(得分:0)

看来CallerMemberNameAttribute类确实是您真正需要的。

答案 1 :(得分:0)

在发行版本中,JIT编译器执行的优化之一是内联方法,即将被调用方法的代码直接插入到调用它的方法中。这意味着某些方法可能会从您看到的堆栈跟踪中消失。

您可以做的是将参数与caller information attributes一起添加到Info方法中,这些参数是针对这种情况而设计的。 C#编译器将在编译时自动填充这些参数的信息,因此它们不受运行时优化的影响。