在C#中打印源文件名和行号

50

有没有办法在 C# 代码中获取当前源文件名和行号,并将该值打印在控制台输出中?就像 C 语言中的 LINEFILE 一样?

请给予指导。

非常感谢!


请问您能提供更多上下文吗?您使用这些变量的目的是什么? - ZombieSheep
为了能够实现我的调试日志的有用信息提示。 - domlao
1
@krakat,现在有更好的方法来做这件事,请查看我的答案。 - illegal-immigrant
6个回答

92

Anders Hejlsberg在BUILD主题演讲中介绍了用于此项任务的新API:

打印当前文件名方法名行号

private static void Log(string text,
                        [CallerFilePath] string file = "",
                        [CallerMemberName] string member = "",
                        [CallerLineNumber] int line = 0)
{
    Console.WriteLine("{0}_{1}({2}): {3}", Path.GetFileName(file), member, line, text);
}

测试:

Log(".NET rocks!");

输出:

Program.cs_Main(11): .NET很棒!

这里发生了什么事情?

你定义了一个带有可选参数的方法,并使用特殊属性进行装饰。如果您调用方法而没有传递实际参数(保留默认值)-框架将为您填充它们。


3
我认为调用者信息中最重要的部分是:“在编译时,调用者信息值作为字面量传递到中间语言(IL)中。与异常的StackTrace属性结果不同,这些结果不受混淆的影响。” 不需要再使用反射来获取此信息! - binki
7
为了让这个有效,你需要包含:using System.Runtime.CompilerServices; - J. Volkya
5
自MSDN:自4.5版本以来可用。 - Radim Cernej
CallerMemberName 很棒,但缺少 CallerTypeName 属性既令人困惑又让人恼火。 - Dai

39

这个答案已经过时了!请参考@taras的答案获取更近期的信息。


没有常量 :(

你能做的只有一个更丑陋的选择:

string currentFile = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileName(); 
int currentLine = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileLineNumber(); 

仅在有PDB文件可用时起作用。


我本来也想发类似的内容,但如果你尝试在发布版本中使用这段代码,会出现问题。不过在调试模式下它能够完美运行。如果你需要使用它,请确保只在调试版本中包含它。 - ZombieSheep
哦,是的!抱歉,我的错。 :) - ZombieSheep
1
@ZombieSheep 和 @Heandel,关键不在于代码是以调试模式还是发布模式构建的,而在于 PDB 文件是否可用,因为它们包含了调试信息。默认情况下,Debug 和 Release 都会生成 PDB 文件。Debug PDB 包含完整的调试信息,而 Release 则设置为仅包含文件名和文件号的 pdb-only,但仍足够使用。 - Bronumski

1

您可以使用System.Diagnostics命名空间中的StackTrace对象,但是只有在存在PDB文件时才能获得信息。

PDB文件默认情况下会为Debug和Release版本生成,唯一的区别是Debug版本设置为生成完整的调试信息,而Release版本设置为仅生成pdb(完整/pdb-only)。

Console.WriteLine(new StackTrace(true).GetFrame(0).GetFileName());

Console.WriteLine(new StackTrace(true).GetFrame(0).GetFileLineNumber());

1

目前还没有为此定义常量。

.NET的处理方式是使用StackTrace类。

然而,它仅适用于调试版本。因此,在使用时,您可以在代码之间使用StackTrace。

#if DEBUG
    //your StackTrace code here
#endif

您可以在以下Stackoverflow主题中阅读有关在DEBUG vs. RELEASE构建中使用#if预处理器的信息。

C# if/then directives for debug vs release

编辑:如果您仍需要在发布版本中进行调试,请阅读Stackoverflow上的以下答案:

Display lines number in Stack Trace for .NET assembly in Release mode


1
如果您需要更多的内部细节,但不需要特别指定文件名和行号,您可以像这样操作:
System.Diagnostics.Debug.Print(this.GetType().ToString() + " My Message");

这种方式比仅打印文件名更有优势,因为如果您将其放在父类中,它将打印出实际运行代码的子类名称。

0

如果你想写自己的Debug.Assert版本,那么这里是一个更完整的答案:

// CC0, Public Domain
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System;

public static class Logger {
    [Conditional("DEBUG")]  
    public static void Assert(bool condition, string msg,
            [CallerFilePath] string file = "",
            [CallerMemberName] string member = "",
            [CallerLineNumber] int line = 0
            )
    {
        // Debug.Assert opens a msg box and Trace only appears in
        // a debugger, so implement our own.
        if (!condition)
        {
            // Roughly follow style of C# error messages:
            // > ideone.cs(14,11): error CS1585: Member modifier 'static' must precede the member type and name
            Console.WriteLine($"{file}({line}): assert: in {member}: {msg}");
            // Or more precisely match style with a fake error so error-parsing tools will detect it:
            // Console.WriteLine($"{file}({line}): warning CS0: {msg}");
        }
    }
}

class Program {
    static void Main(string[] args) {
        Logger.Assert(1+1 == 4, "Why not!");
    }
}

在线尝试


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