是否有编译时访问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框架及其日志记录方面来实现至少CallerMemberName
function。 它可以在.NET 2.0下使用,并且很容易添加日志记录和删除日志记录,而不是删除代码中的单行。