Reputation: 8281
I'm writing a C# program using Visual Studio 2010 where I want to write out certain events to a log file and include the line number the code was on when that happened.
I've only found two ways of capturing line numbers - CallerLineNumber, which requires .Net 4.5/C#5 (I'm targeting .Net 4) and StackFrame.GetFileLineNumber, which apparently requires a debug build and pdb file to work properly, and I'm producing a release build and no pdb file.
But here's what I don't get - both of the above are run-time solutions, but line numbers are compile-time entities. Why is a runtime solution necessary?
I could type in the correct line number as a literal constant by just looking at the bottom of the screen where it says something like "ln 175" . . .
LogEvent("It happened at line 175");
but the problem with that is that if I edit any code before line 175 my literal might no longer be correct. But the compiler knows the correct line number and I've used programming languages in the past that could just pop in the correct line number as a compile time constant. (e.g., ANSI C and Microsoft C++ support a predefined macro called
_LINE_
) Is there any way to get C# to do that? If not are there any solutions to my problem?
Upvotes: 17
Views: 2650
Reputation: 4026
CAVEATS: This is NOT an answer to the OP. I know that. But people looking for something similar may find this page.
But VS 2015, C#, .NET Core or .NET 4.5 allow:
using System.Runtime.CompilerServices;
using System.Diagnostics;
public static String CurrentLocation(
[CallerFilePath] string file = null,
[CallerLineNumber] int lineNumber = 0,
[CallerMemberName] string method = null)
{
String location = file;
if (lineNumber != 0)
{
location += "(" + lineNumber.ToString() + ")";
}
if (!String.IsNullOrWhiteSpace(method))
{
location += ": " + method;
}
if (!String.IsNullOrWhiteSpace(location))
{
location += ": ";
}
return location;
}
[UPDATED USAGE]
With usage something like:
Console.Error.WriteLine(CurrentLocation() + "some message");
or
Debug.WriteLine(CurrentLocation() + "some message");
Upvotes: 4
Reputation: 1341
No, C# doesn't have a macro preprocessor or any meta programming features, so there are no "Compile time" solutions in the entire language. But there are 3rd party macro languages out there that you can use, if you have to, but of course it complicates the build process, Visual Studio won't just figure out how to built it by itself.
You can even use the C preprocessor if you want it. (assuming MSVC compiler)
cl.exe /TC /P /C /EP something.cs > something.raw.cs
cl.exe
is the C compiler/TC
tells the C compiler to treat all files as C sources despite their extensions/P
tells the C compiler to only preprocess the file do not compile it/C
preserves the comments/EP
prevents the compiler from generating #line directives, that the C# compiler wouldn't understandThis will allow you to use #include
, #define
and #if
as well as __FILE__
and __LINE__
in your C# program, but again you have to set up Visual Studio to do this additional compilation step, or use a different build system.
Upvotes: 4
Reputation: 59575
In my opinion, line numbers are the wrong approach. Let me explain:
How unique is line number 175? Even if you find a solution, the next question is: which file was it? And it will repeat: if you know the file, you'll ask yourself: which version of the file was it? And you'll integrate the revision number of your source control system. Once you did that, the question will come up: who was calling that code.
If you're debugging an exception, use a debugger and break on first chance exceptions.
If you're analyzing a performance issue, line numbers don't matter, anything unique will do. Try a memory profiler.
If your methods are too long to identify the problems, refactor your code and make the methods shorter. With short methods, you can use an AOP framework like PostSharp with its logging aspect to achieve at least the CallerMemberName
feature. It's available down to .NET 2.0 and it's very easy to add logging and remove logging, not like deleting single lines in your code.
Upvotes: 1
Reputation: 111
One option would be to use the StackTrace class, like so
[Conditional("DEBUG")]
public static void DebugPrintTrace()
{
StackTrace st = new StackTrace(true);
StackFrame sf = st.GetFrame(1);
Console.WriteLine("Trace "
+ sf.GetFileName() + " "
+ sf.GetMethod().Name + ":"
+ sf.GetFileLineNumber() + "\n");
}
Upvotes: 1