如何获得当前行号?


117

这是我想做的一个例子:

MessageBox.Show("Error line number " + CurrentLineNumber);

在上方的代码中CurrentLineNumber,应该是这段代码的源代码中的行号。

我怎样才能做到这一点?


您无法可靠地执行此操作,因为JIT编译器可以进行优化(例如,内联代码),这意味着您的行号将是错误的。
adrianbanks 2012年

1
由于您可以根据需要关闭优化,因此可以可靠地执行此操作。
jwg

Answers:


175

在.NET 4.5 / C#5中,通过编写使用新调用者属性的实用程序方法,可以使编译器为您完成此工作:

static void SomeMethodSomewhere()
{
    ShowMessage("Boo");
}
...
static void ShowMessage(string message,
    [CallerLineNumber] int lineNumber = 0,
    [CallerMemberName] string caller = null)
{
     MessageBox.Show(message + " at line " + lineNumber + " (" + caller + ")");
}

这将显示,例如:

第39行的Boo(SomeMethodSomewhere)

[CallerFilePath]可以告诉您原始代码文件的路径。


非常感谢您的回答。还能学习对象名称吗?哦,我把别的东西弄糊涂了。我想知道的是asp.net 4.5网站。全局错误捕获器。捕获错误导致的对象名称?
MonsterMMORPG

@MonsterMMORPG nope; 我刚才提到的3个
Marc Gravell

1
@MarcGravell是否要求运行时环境也应为4.5还是编译器功能?
kuldeep '17

5
C#设计得很好。它永远不会令我惊讶。谢谢马克!
nmit026

74

使用StackFrame.GetFileLineNumber方法,例如:

private static void ReportError(string message)
{
     StackFrame callStack = new StackFrame(1, true);
     MessageBox.Show("Error: " + message + ", File: " + callStack.GetFileName() 
          + ", Line: " + callStack.GetFileLineNumber());
}

有关更多信息,请参见Scott Hanselman的Blog条目

[编辑:添加以下内容]

对于使用.Net 4.5或更高版本的用户,请考虑System.Runtime.CompilerServices命名空间中的CallerFilePathCallerMethodNameCallerLineNumber属性。例如:

public void TraceMessage(string message,
        [CallerMemberName] string callingMethod = "",
        [CallerFilePath] string callingFilePath = "",
        [CallerLineNumber] int callingFileLineNumber = 0)
{
    // Write out message
}

参数必须是stringfor CallerMemberName和and CallerFilePathintfor CallerLineNumber和必须具有默认值。在方法参数上指定这些属性会指示编译器在编译时在调用代码中插入适当的值,这意味着它可以通过混淆来工作。有关更多信息,请参见呼叫者信息。


@MonsterMMORPG无论是否存在错误,此方法都有效。StackFrame类仅查看调用当前正在执行的方法。StackFrame构造函数的第一个参数是调用深度(1),第二个参数指示需要文件信息。
akton 2012年

3
如果你编译StackFrame的例子时,一定要使用--debug在编译时和运行时
伯纳德·保卢斯

StackFrame在.NET Core中不可用。使用马克·格雷韦尔的答案。
Jesse Chisholm

使用默认值= string.Empty将引发错误“'callingFilePath'的默认参数值必须是编译时常量”
造口

1
@stomy我将示例更改为使用双引号("")代替string.Empty
阿克顿

21

我更喜欢一种内胆,所以:

int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();

8
它需要.pdb文件..我们通常不会生成/复制到生产服务器。
迪帕克·夏尔马

4

对于那些需要.NET 4.0+方法解决方案的人:

using System;
using System.IO;
using System.Diagnostics;

public static void Log(string message) {
   StackFrame stackFrame = new System.Diagnostics.StackTrace(1).GetFrame(1);
   string fileName = stackFrame.GetFileName();
   string methodName = stackFrame.GetMethod().ToString();
   int lineNumber = stackFrame.GetFileLineNumber();

   Console.WriteLine("{0}({1}:{2})\n{3}", methodName, Path.GetFileName(fileName), lineNumber, message);
}

通话方式:

void Test() {
   Log("Look here!");
}

输出:

无效测试()(FILENAME.cs:104)

看这里!

更改Console.WriteLine的格式!


3
在所有情况下都无法使用。.它始终需要.pdb文件,我们通常不会生成/复制到生产服务器。尝试使用C#5.0 Caller *属性。
Deepak Sharma 2015年

2
如果改用此方法:System.Diagnostics.Debug.WriteLine(String.Format("{0}({1}): {2}: {3}", fileName, lineNumber, methodName, message));则可以在输出窗口中单击该行,并转到源代码中的该行。
Jesse Chisholm

3

如果它在try catch块中,请使用它。

try
{
    //Do something
}
catch (Exception ex)
{
    System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);
    Console.WriteLine("Line: " + trace.GetFrame(0).GetFileLineNumber());
}

1

在.NET 4.5中,您可以通过创建以下函数来获取行号:

static int LineNumber([System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0)
{
    return lineNumber; 
}

这样,每次您拨打电话时,LineNumber()您都会拥有当前线路。与使用StackTrace的任何解决方案相比,这具有优势,它可以同时在调试和发布中工作。

因此,以原始要求为前提,它将变成:

MessageBox.Show("Error enter code here line number " + LineNumber());

这是基于Marc Gravell的出色回答。


这不会返回正确的行号。由于某种原因,我必须减去191以使其正确。
丹尼尔(Daniel)

有趣。对我来说很好。您是否在IDE中打开了链接号?如果从文件中的不同位置调用此函数,您是否还必须减去191?这可能是编译器错误(不太可能,但可能),也可能是页面上的折叠块(虽然这不应该阻止行号不正确,但是如果您要计数而不是查找行号,则可能会解释两者之间的差异)。如果您可以离线与我联系,那么我想深入了解它。
布赖恩·克莱尔

没有折叠的块,行号已打开,无论从何处调用,仍必须减去191。我知道...很奇怪。
丹尼尔(Daniel)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.