user316117
user316117

Reputation: 8281

Is there compile-time access to line numbers in C#?

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

Answers (4)

Jesse Chisholm
Jesse Chisholm

Reputation: 4026

CAVEATS: This is NOT an answer to the OP. I know that. But people looking for something similar may find this page.

  • This is not about .NET 4.
  • This is still ultimately a run-time solution.

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

Evan Dark
Evan Dark

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 understand

This 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

Thomas Weller
Thomas Weller

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

orbifold
orbifold

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

Related Questions