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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/696218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 13:51:01  来源:igfitidea点击:

Do __LINE__ __FILE__ equivalents exist in C#?

c#logging

提问by ojblass

For logging purposes

用于记录目的

__LINE__ 
__FILE__ 

were my friends in C/C++. In Java to get that information I had to throw an exception and catch it. Why are these old standbys so neglected in the modern programming languages? There is something magical about their simplicity.

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

采纳答案by Ed S.

It is uglier, but you can do something like this in C# using the StackTraceand StackFrameclasses:

它更丑陋,但您可以使用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());

Of course, this comes with some overhead.

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

回答by Daniel Earwicker

The closest thing to those is the fact that you can create a StackTraceobject and find out the name of the method at the top of the stack, so you can get close to the functionality of the __FUNCTION__macro.

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

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

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

To reduce the cost of typing this out by hand, and also the runtime code, you can write a helper method:

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

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

Note how we get frame 1, as frame 0 would be LogMethodNameitself. By marking it as Conditional("Debug") we ensure that the code is removed from release builds, which is one way to avoid the runtime cost where it may not be needed.

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

回答by Ender

Because the stack trace contains most of what you need. It will not give you the name of the file but it will give you the class/method name. It also contains the line number. It is not neglected it is automatic. You just need to throw an exception like you do it in Java

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

回答by Igor Brejc

Here's a way to get the line number: http://askville.amazon.com/SimilarQuestions.do?req=line-numbers-stored-stack-trace-C%2523-application-throws-exception

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

If you use log4net, you can get the line number and file name in your logs, but:

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

  • it can decrease your app. performance
  • you have to have .PDB files together with your assemblies.
  • 它可以减少您的应用程序。表现
  • 您必须将 .PDB 文件与程序集一起使用。

回答by Theo Lenndorff

There are already some suggestions to achieve what you want. Either use the StackTrace object or better log4net.

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

In Java to get that information I had to throw an exception and catch it.

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

That's not quite true. You can have it without throwing exceptions, too. Have a look to log4j. It even logs your method and class name, without polluting your code with hard coded strings containing the current method name (at least I have seen this in some occasions).

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

Why are these old standbys so neglected in the modern programming languages?

为什么这些旧的备用数据库在现代编程语言中如此被忽视?

Java and C# don't make use (in the latter: excessive use) of preprocessors. And I think it's good. Abusing preprocessors to make unreadable code is very easy. And if programmers canabuse some technique ... they willabuse it.

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

Just a note about performance, which is very likely to be the next thing, which pops up in your mind:

只是一个关于性能的说明,这很可能是接下来会出现在你脑海中的事情:

If you use StackTrace or log4net you will always will read or hear that it is slow, because it uses Reflection. I am using log4net and I never encountered logging as a performance bottle neck. If it would be, I can declaratively deactivate (parts of) logging -- without changing the source code. That's pure beauty compared to delete all the logging lines in C/C++ code! (Besides: If performance is a primary goal, I would use C/C++ ... it will never die despite of Java and C#.)

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

回答by roken

Caller Informationhas been added to .NET 4.5. This will be compiled, a big improvment over having to examine the stacktrace manually.

呼叫者信息已添加到 .NET 4.5。这将被编译,与手动检查堆栈跟踪相比,这是一个很大的改进。

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

Simply call it in this manner, the compiler will fill in the filename and line number for you:

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

logger.Log("Hello!");

回答by Brian Cryer

With Caller Information(introduced in .NET 4.5) you can create the equivalent of __LINE__and __FILE__in C#:

使用调用者信息(在 .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;
}

The only thing to remember is that these are functions and not compiler directives.

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

So for example:

例如:

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

If you were to use this in practise then I'd suggest different names. I've used the C/C++ names just to make it clearer what they are returning, and something like CurrentLineNumber()and CurrentFileName()might be better names.

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

The advantage of using Caller Information over any solution that uses the StackTraceis that the line and file information is available for both debug and release.

使用调用者信息优于任何使用 的解决方案的优点StackTrace是行和文件信息可用于调试和发布。

回答by JonN

Having used the FILE and LINE macros for many years for logging in C/C++ I really wanted a similar solution in C#. This is my solution. I prefer it to @fostandy suggestion of creating many overloads with varying number of parameters. This seems the less intrusive and does not limit the number of formatted parameters. You just have to be willing to accept the addition of the F.L() parameter at start of every Log.Msg() call.

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

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

Here's the output from this code above

这是上面这段代码的输出

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