是否有编译时访问C#中的行号?

我正在使用Visual Studio 2010编写一个C#程序,我希望将某些事件写入日志文件,并包含代码所在的行号。

我只发现了两种捕获行号的方法 – CallerLineNumber,它需要.Net 4.5 / C#5(我的目标是.Net 4)和StackFrame.GetFileLineNumber,这显然需要调试版本和pdb文件才能正常工作,我正在生成一个发布版本,没有pdb文件。

但这是我没有得到的 – 以上都是运行时解决方案,但行号是编译时实体。 为什么需要运行时解决方案?

我可以输入正确的行号作为文字常量,只需查看屏幕底部的内容,如“ln 175”。 。 。

LogEvent("It happened at line 175"); 

但问题是,如果我在第175行之前编辑任何代码,我的文字可能不再正确。 但编译器知道正确的行号,并且我过去曾使用过编程语言,它们只能将正确的行号作为编译时常量弹出。 (例如,ANSI C和Microsoft C ++支持一个名为_LINE_的预定义宏)有没有办法让C#这样做? 如果没有解决我的问题?

不,C#没有宏预处理器或任何元编程function,因此整个语言中没有“编译时”解决方案。 但是你可以使用第三方宏语言,如果必须的话,但当然它使构建过程变得复杂,Visual Studio不仅会弄清楚如何自己构建它。

如果需要,您甚至可以使用C预处理器。 (假设MSVC编译器)

 cl.exe /TC /P /C /EP something.cs > something.raw.cs 
  • cl.exe是C编译器
  • /TC告诉C编译器将所有文件视为C源,尽管它们有扩展
  • /P告诉C编译器只预处理文件而不编译它
  • /C保留评论
  • /EP阻止编译器生成#line指令,C#编译器无法理解

这将允许您在C#程序中使用#include#if #define#if以及__FILE____LINE__ ,但是您还必须设置Visual Studio来执行此额外的编译步骤,或者使用不同的构建系统。

CAVEATS :这不是OP的答案。 我知道。 但是寻找类似东西的人可能会找到这个页面。

  • 这不是关于.NET 4。
  • 这仍然是最终的运行时解决方案。

但VS 2015,C#,.NET Core或.NET 4.5允许:

 using System.Runtime.CompilerServices; using System.Diagnostics; public static String CurrentLocation( [CallerFilePath] string file = null, [CallerLineNumber] int lineNumber = 0, [CallerMemberName] string method = null) { String location = file; if (lineNumber != 0) { location += "(" + lineNumber.ToString() + ")"; } if (!String.IsNullOrWhiteSpace(method)) { location += ": " + method; } if (!String.IsNullOrWhiteSpace(location)) { location += ": "; } return location; } 

[更新用途]

使用类似于:

 Console.Error.WriteLine(CurrentLocation() + "some message"); 

要么

 Debug.WriteLine(CurrentLocation() + "some message"); 

一种选择是使用StackTrace类 ,就像这样

  [Conditional("DEBUG")] public static void DebugPrintTrace() { StackTrace st = new StackTrace(true); StackFrame sf = st.GetFrame(1); Console.WriteLine("Trace " + sf.GetFileName() + " " + sf.GetMethod().Name + ":" + sf.GetFileLineNumber() + "\n"); } 

在我看来,行号是错误的方法。 让我解释:

第175行有多独特? 即使您找到了解决方案,下一个问题是:它是哪个文件? 它会重复:如果您知道该文件,您会问自己:该文件的版本是什么? 并且您将集成源控制系统的修订号。 一旦你这样做了,问题就出现了:谁在调用那段代码。

如果您正在调试exception,请使用调试器并中断第一次机会exception。

如果您正在分析性能问题,行号无关紧要,任何独特的都行。 试试一个内存分析器。

如果您的方法太长而无法识别问题,请重构代码并缩短方法。 使用简短方法,您可以使用像PostSharp这样的AOP框架及其日志记录方面来实现至少CallerMemberNamefunction。 它可以在.NET 2.0下使用,并且很容易添加日志记录和删除日志记录,而不是删除代码中的单行。