Question

Here is an example of what I want to do:

MessageBox.Show("Error line number " + CurrentLineNumber);

In the code above the CurrentLineNumber, should be the line number in the source code of this piece of code.

How can I do that?

Was it helpful?

Solution

In .NET 4.5 / C# 5, you can get the compiler to do this work for you, by writing a utility method that uses the new caller attributes:

using System.Runtime.CompilerServices;

static void SomeMethodSomewhere()
{
    ShowMessage("Boo");
}
...
static void ShowMessage(string message,
    [CallerLineNumber] int lineNumber = 0,
    [CallerMemberName] string caller = null)
{
     MessageBox.Show(message + " at line " + lineNumber + " (" + caller + ")");
}

This will display, for example:

Boo at line 39 (SomeMethodSomewhere)

There's also [CallerFilePath] which tells you the path of the original code file.

OTHER TIPS

Use the StackFrame.GetFileLineNumber method, for example:

private static void ReportError(string message)
{
     StackFrame callStack = new StackFrame(1, true);
     MessageBox.Show("Error: " + message + ", File: " + callStack.GetFileName() 
          + ", Line: " + callStack.GetFileLineNumber());
}

See Scott Hanselman's Blog entry for more information.

[Edit: Added the following]

For those using .Net 4.5 or later, consider the CallerFilePath, CallerMethodName and CallerLineNumber attributes in the System.Runtime.CompilerServices namespace. For example:

public void TraceMessage(string message,
        [CallerMemberName] string callingMethod = "",
        [CallerFilePath] string callingFilePath = "",
        [CallerLineNumber] int callingFileLineNumber = 0)
{
    // Write out message
}

The arguments must be string for CallerMemberName and CallerFilePath and an int for CallerLineNumber and must have a default value. Specifying these attributes on method parameters instructs the compiler to insert the appropriate value in the calling code at compile time, meaning it works through obfuscation. See Caller Information for more information.

I prefer one liners so:

int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();

For those who need a .NET 4.0+ method solution:

using System;
using System.IO;
using System.Diagnostics;

public static void Log(string message) {
   StackFrame stackFrame = new System.Diagnostics.StackTrace(1).GetFrame(1);
   string fileName = stackFrame.GetFileName();
   string methodName = stackFrame.GetMethod().ToString();
   int lineNumber = stackFrame.GetFileLineNumber();

   Console.WriteLine("{0}({1}:{2})\n{3}", methodName, Path.GetFileName(fileName), lineNumber, message);
}

How to call:

void Test() {
   Log("Look here!");
}

Output:

Void Test()(FILENAME.cs:104)

Look here!

Change the Console.WriteLine format how you like!

In .NET 4.5 you can get the line number by creating the function:

static int LineNumber([System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0)
{
    return lineNumber; 
}

Then each time you call LineNumber() you will have the current line. This has the advantage over any solution using the StackTrace that it should work in both debug and release.

So taking the original request of what is required, it would become:

MessageBox.Show("Error enter code here line number " + LineNumber());

This is building on the excellent answer by Marc Gravell.

If its in a try catch block use this.

try
{
    //Do something
}
catch (Exception ex)
{
    System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);
    Console.WriteLine("Line: " + trace.GetFrame(0).GetFileLineNumber());
}

You only asked about the line number and with nullable project type, you then need to use a something like this

internal class Utils
{
   public static int Line([CallerLineNumber] int? lineNumber =null)=>lineNumber;
}

in your code, if you like to get a line number you then just call

var line=Utils.Line();

if you are logging and you would like to document the line number in say logging than call the method like this

public void MyMethod(int someValue)
{
    switch(someValue)
    { 
        case 1:
            if(abc<xyz)
            {
             logger.LogInformation("case value {someValue} this line {line} was true", someValue ,Utils.Line()-2);
            }
            break;

        case 2:
           logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
           break;
        caste 3:
           logger.LogInformation("case value {someValue} this {line} was executed",someValue,Utils.Line());
           break;
    }
}

You can extend this pattern with any of the other [CallerXXX] methods and not use them where ever, not just in the method parameters.

in the Nuget Package Walter I use a super cool class named ExceptionObject

if you import the NuGet package you have some nice extension methods on the Exception class as well as access to a CallStack showing the call chain including method parameters and parameter values of all methods called.

It's like a stack of an exception only with values showing how you got where you got with what values.

public void MyMethod()
{
    try
    {
    
      //get me all methods, signatures, parameters line numbers file names etc used as well as assembly info of all assemblies used for documentation of how the code got here
      var stack= new CallStack();
      foreach( var frame in StackedFrames)
      {
         logger.LogDebug(frame.ToString());
      }
    }
    catch(SqlException e)
    {
       var ex = new ExceptionObject(e);
       logger.LogException(e,"{this} exception due to {message} {server} {procedure} TSQL-line:{sqlline}\n{TSQL}"
                            ,e.GetType().Name
                            ,e.Message
                            ,ex.SqlServer
                            ,ex.SqlProcedureName
                            ,ex.SqlLineNumber
                            ,ex.Tsql
                            ,ex.CallStack); 
    }
    catch(Exception e)
    {
       var ex = new ExceptionObject(e);
       logger.LogException(e,"{this} exception due to {message} signature: signature}\nCallStack:", e.GetType().Name,e.Message,ex.Signature,ex.CallStack); 
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top