148

这是我想做的一个例子:

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

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

我怎样才能做到这一点?

4

7 回答 7

214

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

using System.Runtime.CompilerServices;

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 行的嘘声(SomeMethodSomewhere)

还有[CallerFilePath]一个告诉你原始代码文件的路径。

于 2013-01-02T13:17:18.017 回答
88

使用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 的博客条目

[编辑:添加以下内容]

对于使用 .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
}

参数必须是stringforCallerMemberNameCallerFilePath一个intforCallerLineNumber并且必须有一个默认值。在方法参数上指定这些属性会指示编译器在编译时在调用代码中插入适当的值,这意味着它通过混淆工作。有关详细信息,请参阅来电者信息。

于 2012-09-23T22:26:40.823 回答
23

我更喜欢一个衬垫,所以:

int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
于 2013-05-29T15:20:31.343 回答
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!");
}

输出:

无效测试()(文件名.cs:104)

看这里!

随意更改 Console.WriteLine 格式!

于 2014-06-25T17:47:51.517 回答
4

在 .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 的出色回答的基础上的。

于 2017-05-23T15:17:04.573 回答
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());
}
于 2012-09-23T22:28:39.827 回答
0

您只询问了行号和可为空的项目类型,然后您需要使用类似这样的东西

internal class Utils
{
   public static int Line([CallerLineNumber] int? lineNumber =null)=>lineNumber;
}

在您的代码中,如果您想获取行号,则只需致电

var line=Utils.Line();

如果您正在记录并且您想在记录中记录行号而不是调用这样的方法

public void MyMethod(int someValue)
{
    switch(someValue)
    { 
        case 1:
            if(abc<xyz)
            {
             logger.LogInformation("case value {someValue} this line {line} was true", someValue ,Utils.Line()-2);
            }
            break;

        case 2:
           logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
           break;
        caste 3:
           logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
           break;
    }
}

您可以使用任何其他 [CallerXXX] 方法扩展此模式,而不是在任何地方使用它们,而不仅仅是在方法参数中。

在 Nuget Package Walter中,我使用了一个名为 ExceptionObject 的超酷类

如果您导入 NuGet 包,您将在 Exception 类上有一些不错的扩展方法,并且可以访问显示调用链的 CallStack,包括方法参数和所有调用方法的参数值。

这就像一堆异常,只有值显示了你是如何得到什么值的。

public void MyMethod()
{
    try
    {
    
      //get me all methods, signatures, parameters line numbers file names etc used as well as assembly info of all assemblies used for documentation of how the code got here
      var stack= new CallStack();
      foreach( var frame in StackedFrames)
      {
         logger.LogDebug(frame.ToString());
      }
    }
    catch(SqlException e)
    {
       var ex = new ExceptionObject(e);
       logger.LogException(e,"{this} exception due to {message} {server} {procedure} TSQL-line:{sqlline}\n{TSQL}"
                            ,e.GetType().Name
                            ,e.Message
                            ,ex.SqlServer
                            ,ex.SqlProcedureName
                            ,ex.SqlLineNumber
                            ,ex.Tsql
                            ,ex.CallStack); 
    }
    catch(Exception e)
    {
       var ex = new ExceptionObject(e);
       logger.LogException(e,"{this} exception due to {message} signature: signature}\nCallStack:", e.GetType().Name,e.Message,ex.Signature,ex.CallStack); 
    }
}
于 2020-09-11T16:00:09.263 回答