C#中是否存在__LINE__ __FILE__等价物?

时间:2009-03-30 06:45:21

标签: c# logging

用于记录目的

__LINE__ 
__FILE__ 

是我在C / C ++中的朋友。在Java中获取该信息,我不得不抛出异常并抓住它。为什么在现代编程语言中忽略了这些旧的备用语言?他们的朴素有一些神奇的东西。

8 个答案:

答案 0 :(得分:73)

Caller Information已添加到.NET 4.5中。这将被编译,这是一个必须手动检查堆栈跟踪的重大改进。

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

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

logger.Log("Hello!");

答案 1 :(得分:26)

这很丑陋,但您可以使用StackTraceStackFrame类在C#中执行以下操作:

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());

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

答案 2 :(得分:11)

最接近这一点的是你可以创建一个StackTrace对象并在堆栈顶部找出方法的名称,这样你就可以接近{{1 }。宏。

__FUNCTION__

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

StackTrace stackTrace = new StackTrace();           
StackFrame[] stackFrames = stackTrace.GetFrames();  

foreach (StackFrame stackFrame in stackFrames)
    Console.WriteLine(stackFrame.GetMethod().Name);   

注意我们如何得到第1帧,因为第0帧本身就是[Conditional("Debug")] public void LogMethodName() { Trace.WriteLine("Entering:" + new StackTrace().GetFrame(1).GetMethod().Name); } 。通过将其标记为条件(“调试”),我们确保从发布版本中删除代码,这是避免可能不需要的运行时成本的一种方法。

答案 3 :(得分:6)

使用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()之类的东西可能是更好的名字。

使用Caller Information而不是使用StackTrace的任何解决方案的优势在于,行和文件信息可用于调试和发布。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

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

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

答案 6 :(得分:0)

已经有一些建议可以达到你想要的效果。使用StackTrace对象或更好的log4net。

在Java中获取该信息我不得不抛出异常并抓住它。

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

为什么现代编程语言中忽略了这些旧的备用语言?

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

只是关于性能的一个注释,这很可能是下一件事,它会出现在你脑海中:

如果您使用StackTrace或log4net,您将始终会读取或听到它很慢,因为它使用Reflection。我正在使用log4net,我从来没有遇到过记录作为性能瓶颈。如果是这样,我可以声明性地停用(部分)日志记录 - 而无需更改源代码。与删除C / C ++代码中的所有日志行相比,这是纯粹的美! (另外:如果性能是主要目标,我会使用C / C ++ ......尽管有Java和C#,但它永远不会消亡。)

答案 7 :(得分:0)

多年来使用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