【问题标题】:Do __LINE__ __FILE__ equivalents exist in C#?C# 中是否存在 __LINE__ __FILE__ 等价物?
【发布时间】:2009-03-30 06:45:20
【问题描述】:

用于记录目的

__LINE__ 
__FILE__ 

是我的 C/C++ 朋友。在 Java 中,为了获得这些信息,我必须抛出一个异常并捕获它。为什么在现代编程语言中如此忽视这些旧备用?它们的简单性有一些神奇之处。

【问题讨论】:

  • 只是为了提供一些上下文,__LINE____FILE__ 需要宏预处理,因为它容易被残酷地滥用,所以从 C# 中明确删除了它。虽然我也很想拥有这些构造,但我当然可以理解为什么他们决定不提供它们(从成本/收益的角度来看)

标签: c# logging


【解决方案1】:

Caller Information 已添加到 .NET 4.5。这将被编译,与手动检查堆栈跟踪相比有了很大改进。

public void Log(string message,
        [CallerFilePath] string filePath = "",
        [CallerLineNumber] int lineNumber = 0)
{
    // Do logging
}

只需以这种方式调用它。编译器会为你填写文件名和行号:

logger.Log("Hello!");

【讨论】:

  • 虽然我认为这是一个不错的解决方案,但它导致了一个常用模式的问题:Log(string format, params object[] args),因为 params 必须是最后一个参数。除了将字符串格式负担转移给调用者之外,有什么解决方案吗?
  • @OregonGhost 是的,我制作了一个带有一堆重载的包装类,例如Log(string format, [CallerFilePath]..)Log(string format, object o1, [CallerFilePath]...)Log(string format, object o1, object o2, [CallerFilePath]...) 等。这令人不快且不理想,但这是迄今为止我找到的最实用的解决方案。
  • @fostandy:好吧,听起来像是一个不错的可重用解决方案,除非你有很多参数:)
【解决方案2】:

它更丑,但你可以在 C# 中使用 StackTraceStackFrame 类做这样的事情:

StackTrace st = new StackTrace(new StackFrame(true));
Console.WriteLine(" Stack trace for current level: {0}", st.ToString());
StackFrame sf = st.GetFrame(0);
Console.WriteLine(" File: {0}", sf.GetFileName());
Console.WriteLine(" Method: {0}", sf.GetMethod().Name);
Console.WriteLine(" Line Number: {0}", sf.GetFileLineNumber());
Console.WriteLine(" Column Number: {0}", sf.GetFileColumnNumber());

当然,这会带来一些开销。

【讨论】:

  • ...因此,您应该真正将这种代码包装在 #if DEBUG #endif 块中
  • 我需要了解 C# 应用程序中的条件编译,但可以获得的信息比我想象的还要多。我还必须了解宏及其在 C# 中的支持。谢谢你的回答。
  • 宏又是预处理器的一个特性。在编译之前,它们的宏名称在代码中被宏体替换。 C# 中没有宏。
【解决方案3】:

使用 Caller Information(在 .NET 4.5 中引入),您可以在 C# 中创建 __LINE____FILE__ 的等效项:

static int __LINE__([System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0)
{
    return lineNumber;
}
static string __FILE__([System.Runtime.CompilerServices.CallerFilePath] string fileName = "")
{
    return fileName;
}

唯一要记住的是,这些是函数,而不是编译器指令。

例如:

MessageBox.Show("Line " + __LINE__() + " in " + __FILE__());

如果您要在实践中使用它,那么我建议您使用不同的名称。我使用 C/C++ 名称只是为了更清楚地说明它们返回的内容,CurrentLineNumber()CurrentFileName() 之类的名称可能更好。

与使用StackTrace 的任何解决方案相比,使用调用者信息的优势在于行和文件信息可用于调试和发布。

【讨论】:

    【解决方案4】:

    最接近这些的是您可以创建一个StackTrace 对象并在堆栈顶部找出方法的名称,这样您就可以接近__FUNCTION__ 宏的功能。

    StackTrace stackTrace = new StackTrace();           
    StackFrame[] stackFrames = stackTrace.GetFrames();  
    
    foreach (StackFrame stackFrame in stackFrames)
        Console.WriteLine(stackFrame.GetMethod().Name);   
    

    为了减少手动输入的成本以及运行时代码,您可以编写一个辅助方法:

    [Conditional("Debug")]
    public void LogMethodName()
    {
        Trace.WriteLine("Entering:" + new StackTrace().GetFrame(1).GetMethod().Name);
    }
    

    注意我们是如何得到第 1 帧的,因为第 0 帧本身就是LogMethodName。通过将其标记为 Conditional("Debug"),我们确保从发布版本中删除代码,这是避免可能不需要的运行时成本的一种方法。

    【讨论】:

      【解决方案5】:

      因为堆栈跟踪包含您需要的大部分内容。它不会给你文件名,但会给你类/方法名。它还包含行号。它不会被忽视,它是自动的。你只需要像在 Java 中那样抛出一个异常

      【讨论】:

      • 您不需要抛出异常 - 请参阅 Ed Swangren 的回答。问题是这比在构建时嵌入信息要慢得多。
      【解决方案6】:

      以下是获取行号的方法:http://askville.amazon.com/SimilarQuestions.do?req=line-numbers-stored-stack-trace-C%2523-application-throws-exception

      如果你使用log4net,你可以在你的日志中获取行号和文件名,但是:

      • 它可以减少您的应用程序。性能
      • 您必须将 .PDB 文件与您的程序集一起使用。

      【讨论】:

      • 答案中的链接已失效 (DNS_PROBE_FINISHED_NXDOMAIN)。
      【解决方案7】:

      已经有一些建议可以实现您想要的。使用 StackTrace 对象或更好的 log4net。

      在 Java 中,为了获取该信息,我必须抛出异常并捕获它。

      这并不完全正确。你也可以在不抛出异常的情况下拥有它。看看 log4j。它甚至会记录您的方法和类名称,而不会使用包含当前方法名称的硬编码字符串污染您的代码(至少我在某些情况下见过这种情况)。

      为什么现代编程语言如此忽视这些旧备用?

      Java 和 C# 不使用(在后者中:过度使用)预处理器。而且我认为这很好。滥用预处理器来制作不可读的代码非常容易。如果程序员可以滥用某些技术......他们滥用它。

      只是一个关于性能的注释,很可能是接下来会出现在你脑海中的东西:

      如果您使用 StackTrace 或 log4net,您总是会读到或听到它很慢,因为它使用反射。我正在使用 log4net,但我从未遇到过日志记录作为性能瓶颈。如果是这样,我可以声明式地停用(部分)日志记录——而不更改源代码。与删除 C/C++ 代码中的所有日志记录行相比,这简直太美了! (此外:如果性能是主要目标,我会使用 C/C++ ...尽管有 Java 和 C#,但它永远不会消失。)

      【讨论】:

      • 有用于 C++ 的 log4cxx 库,与 log4net 和 log4j 一样,您可以声明式地停用(部分)日志记录——无需更改源代码。 :)
      【解决方案8】:

      多年来我一直使用 FILE 和 LINE 宏来登录 C/C++,我真的很想在 C# 中使用类似的解决方案。这是我的解决方案。我更喜欢 @fostandy 建议使用不同数量的参数创建许多重载。这似乎不那么具有侵入性,并且不限制格式化参数的数量。您只需要愿意接受在每个 Log.Msg() 调用开始时添加 F.L() 参数即可。

      using System;
      using System.Runtime.CompilerServices;
      
      namespace LogFileLine
      {
          public static class F
          {
              // This method returns the callers filename and line number
              public static string L([CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
              {
                  // Remove path leaving only filename
                  while (file.IndexOf("\\") >= 0)
                      file = file.Substring(file.IndexOf("\\")+1);
      
                  return String.Format("{0} {1}:", file, line); 
              }
          }
      
          public static class Log
          {
              // Log a formatted message. Filename and line number of location of call
              // to Msg method is automatically appended to start of formatted message.
              // Must be called with this syntax:
              // Log.Msg(F.L(), "Format using {0} {1} etc", ...);
              public static void Msg(string fileLine, string format, params object[] parms)
              {
                  string message = String.Format(format, parms);
                  Console.WriteLine("{0} {1}", fileLine, message);
              }
          }
      
          class Program
          {
              static void Main(string[] args)
              {
                  int six = 6;
                  string nine = "nine";
                  string dog = "dog";
                  string cat = "cats";
      
                  Log.Msg(F.L(), "The {0} chased the {1} {2}", dog, 5, cat);
      
                  Log.Msg(F.L(), "Message with no parameters");
      
                  Log.Msg(F.L(), "Message with 8 parameters {0} {1} {2} {3} {4} {5} {6} {7}",
                      1, 2, 3, "four", 5, 6, 7, 8.0);
      
                  Log.Msg(F.L(), "This is a message with params {0} and {1}", six, nine);
              }
          }
      }
      

      这是上面这段代码的输出

      Program.cs 41: The dog chased the 5 cats
      Program.cs 43: Message with no parameters
      Program.cs 45: Message with 8 parameters 1 2 3 four 5 6 7 8
      Program.cs 48: This is a message with params 6 and nine
      

      【讨论】:

        猜你喜欢
        • 2011-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-15
        • 1970-01-01
        • 2011-05-24
        • 1970-01-01
        • 2013-04-19
        相关资源
        最近更新 更多