163

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?

Twenty
  • 5,234
  • 4
  • 32
  • 67
Furkan Gözükara
  • 22,964
  • 77
  • 205
  • 342
  • You can't _reliably_ do this, as the JIT compiler can make optimisations (e.g. inlining code), meaning that your line numbers will be wrong. – adrianbanks Sep 23 '12 at 22:40
  • 1
    Since you can turn off optimization if you want to, you _can_ reliably do this. – jwg Feb 05 '13 at 09:21
  • 1
    Possible duplicate of [Print the source filename and linenumber in C#](http://stackoverflow.com/questions/6369184/print-the-source-filename-and-linenumber-in-c-sharp) – Michael Freidgeim Aug 18 '16 at 04:02

7 Answers7

236

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.

James Hirschorn
  • 7,032
  • 5
  • 45
  • 53
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • thanks a lot for the answer. is that possible to learn also object name ? oh i confused with something else. what i wonder is asp.net 4.5 website. global error catcher. catch the error caused object name ? – Furkan Gözükara Jan 03 '13 at 04:24
  • @MonsterMMORPG nope; just the 3 I mentioned above – Marc Gravell Jan 03 '13 at 07:46
  • 1
    @MarcGravell does this require that Run time environment is also should be 4.5 OR is it a compiler feature ? – kuldeep Feb 24 '17 at 14:17
  • For the benefit of lurkers, you must include using System.Runtime.CompilerServices at the top of the class in which the utility method is defined. – David A. Gray Apr 25 '22 at 15:00
91

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.

akton
  • 14,148
  • 3
  • 43
  • 47
  • @MonsterMMORPG This works irrespective of whether there is an error or not. The StackFrame class is just looking at the method calling the current being executed. The first argument to the StackFrame constructor is the call depth (1) and the second argument indicates that file information is needed. – akton Sep 23 '12 at 22:59
  • 3
    If you're compiling the `StackFrame` example on **Mono**, be sure to **use `--debug`** at compile time and at run time – bernard paulus Dec 28 '12 at 10:55
  • `StackFrame` is not available in .NET Core. Use Marc Gravell's answer. – Jesse Chisholm Jan 24 '17 at 02:36
  • Using default value `= string.Empty` throws error _"Default parameter value for 'callingFilePath' must be a compile-time constant"_! – stomy May 06 '19 at 22:11
  • 1
    @stomy I changed the examp[le to use double quotes (`""`) instead of `string.Empty`. – akton May 06 '19 at 22:32
24

I prefer one liners so:

int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
iambriansreed
  • 21,935
  • 6
  • 63
  • 79
6

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.

Brian Cryer
  • 2,126
  • 18
  • 18
  • This does not return the right line number. I have to subtract 191 to get it right for some reason. – Daniel Jul 13 '17 at 16:38
  • Interesting. Works fine here for me. Do you have link numbers turned on in the IDE? If you call this function from different places in the file do you still have to subtract 191? This will either be a compiler bug (unlikely, but possible) or a collapsed block on your page (whilst that shouldn't prevent line numbers from being correct it might explain the difference if you were counting rather than looking up the line number). If you can contact me offline then I'd love to get to the bottom of it. – Brian Cryer Jul 14 '17 at 09:34
  • No collapsed blocks, line numbers are turned on, still have to subtract 191 regardless of where its called from. I know...weird. – Daniel Jul 17 '17 at 19:11
4

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!

Community
  • 1
  • 1
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
  • 3
    will not work in all the cases.. it needs .pdb file always, which we generally do not generate/copy to production server. try with C#5.0 Caller* attribute. – Deepak Sharma Oct 07 '15 at 06:30
  • 2
    If you instead use this: `System.Diagnostics.Debug.WriteLine(String.Format("{0}({1}): {2}: {3}", fileName, lineNumber, methodName, message));` then you can click the line in the output window and be taken to that line in the source. – Jesse Chisholm Jan 14 '16 at 22:16
3

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());
}
Nate-Wilkins
  • 5,364
  • 4
  • 46
  • 61
0

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); 
    }
}
Walter Verhoeven
  • 3,867
  • 27
  • 36