【问题标题】:How do I get the current line number?如何获取当前行号?
【发布时间】:2012-09-15 10:12:00
【问题描述】:

这是我想做的一个例子:

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

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

我该怎么做?

【问题讨论】:

  • 你不能可靠地这样做,因为 JIT 编译器可以进行优化(例如内联代码),这意味着你的行号会出错。
  • 由于您可以根据需要关闭优化,因此您可以可靠地执行此操作。

标签: c# wpf c#-4.0 line-numbers


【解决方案1】:

在 .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]告诉你原始代码文件的路径。

【讨论】:

  • 非常感谢您的回答。也可以学习对象名称吗?哦,我对别的东西感到困惑。我想知道的是asp.net 4.5 网站。全局错误捕获器。捕获错误导致的对象名称?
  • @MonsterMMORPG 不;只是我上面提到的 3 个
  • @MarcGravell 这是否要求运行时环境也应该是 4.5 或者它是编译器功能吗?
【解决方案2】:

使用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's Blog entry

[编辑:添加以下内容]

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

对于CallerMemberNameCallerFilePath,参数必须是string,对于CallerLineNumber,参数必须是int,并且必须具有默认值。在方法参数上指定这些属性会指示编译器在编译时在调用代码中插入适当的值,这意味着它通过混淆工作。请参阅Caller Information 了解更多信息。

【讨论】:

  • @MonsterMMORPG 无论是否有错误,它都有效。 StackFrame 类只是查看调用当前正在执行的方法。 StackFrame 构造函数的第一个参数是调用深度 (1),第二个参数表示需要文件信息。
  • 如果您在 Mono 上编译 StackFrame 示例,请务必在编译时和运行时使用--debug
  • StackFrame 在 .NET Core 中不可用。使用 Marc Gravell 的答案。
  • 使用默认值= string.Empty 会引发错误“'callingFilePath' 的默认参数值必须是编译时常量”
  • @stomy 我将 example[le 更改为使用双引号 ("") 而不是 string.Empty
【解决方案3】:

我更喜欢一个衬里:

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

【讨论】:

  • 它需要.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 格式!

【讨论】:

  • 并非在所有情况下都有效.. 它始终需要 .pdb 文件,我们通常不会生成/复制到生产服务器。尝试使用 C#5.0 Caller* 属性。
  • 如果您改用这个:System.Diagnostics.Debug.WriteLine(String.Format("{0}({1}): {2}: {3}", fileName, lineNumber, methodName, message));,那么您可以单击输出窗口中的行并转到源代码中的该行。
【解决方案5】:

在 .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 才能正确。
  • 有趣。在这里工作对我来说很好。您是否在 IDE 中打开了链接编号?如果您从文件中的不同位置调用此函数,您还需要减去 191 吗?这可能是编译器错误(不太可能,但可能)或页面上的折叠块(虽然这不应阻止行号正确,但如果您正在计数而不是查找行号,它可能会解释差异)。如果您可以离线与我联系,那么我很乐意深入了解它。
  • 没有折叠块,行号打开,不管从哪里调用,仍然要减去 191。我知道……很奇怪。
【解决方案6】:

如果它在 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());
}

【讨论】:

    【解决方案7】:

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

    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 包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); 
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-02-06
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 2016-09-20
      相关资源
      最近更新 更多