24

Considering that the debug data file is available (PDB) and by using either System.Reflection or another similar framework such as Mono.Cecil, how to retrieve programmatically the source file name and the line number where a type or a member of a type is declared.

For example, let's say you have compiled this file into an assembly:

C:\MyProject\Foo.cs

1:    public class Foo
2:    {
3:       public string SayHello()
4:       {
5:           return "Hello";
6:       }
7:    }

How to do something like:

MethodInfo methodInfo = typeof(Foo).GetMethod("SayHello");
string sourceFileName = methodInfo.GetSourceFile(); // ?? Does not exist!
int sourceLineNumber = methodInfo.GetLineNumber(); // ?? Does not exist!

sourceFileName would contain "C:\MyProject\Foo.cs" and sourceLineNumber be equal to 3.

Update: System.Diagnostics.StackFrame is indeed able to get that information, but only in the scope of current executing call stack. It means that the method must be invoked first. I would like to get the same info, but without invoking the type member.

Yann Trevin
  • 3,823
  • 1
  • 30
  • 32
  • there is new API for that, no need to use PDB reader any more, see my answer – illegal-immigrant Apr 30 '13 at 09:01
  • 1
    Ever get this figured out? The answers completely miss the point of your question, which is getting the file name and line number for an ARBITRARY member. Not for the current one... Harold's is the closest, but requires every potential member to be decorated with that attribute... – Douglas Gaskell Oct 10 '19 at 21:16

3 Answers3

11

Up to date method:

private static void Log(string text,
                        [CallerFilePath] string file = "",
                        [CallerMemberName] string member = "",
                        [CallerLineNumber] int line = 0)
{
    Console.WriteLine("{0}_{1}({2}): {3}", Path.GetFileName(file), member, line, text);
}

New Framework API which populates arguments (marked with special attributes) at runtime, see more in my answer to this SO question

Community
  • 1
  • 1
illegal-immigrant
  • 8,089
  • 9
  • 51
  • 84
  • Only available for .Net 4.5 Huge disadvantage for corporate projects. – Kobor42 Sep 25 '13 at 10:26
  • 10
    I think you fundamentally misunderstood the question. You showed how to get information about the caller, but the question asks how to query this for any symbol. See Yann Trevin's comment to Richard's answer. – Ben Voigt Oct 17 '14 at 19:16
3

Using one of the methods explained above, inside the constructor of an attribute, you can provide the source location of everything, that may have an attribute - for instance a class. See the following attribute class:

sealed class ProvideSourceLocation : Attribute
    {
        public readonly string File;
        public readonly string Member;
        public readonly int Line;
        public ProvideSourceLocation
            (
            [CallerFilePath] string file = "",
            [CallerMemberName] string member = "",
            [CallerLineNumber] int line = 0)
        {
            File = file;
            Member = member;
            Line = line;
        }

        public override string ToString() { return File + "(" + Line + "):" + Member; }
    }


[ProvideSourceLocation]
class Test
{
   ...
}

The you can write for instance:

Console.WriteLine(typeof(Test).GetCustomAttribute<ProvideSourceLocation>(true));

Output will be:

a:\develop\HWClassLibrary.cs\src\Tester\Program.cs(65):
Harald Hoyer
  • 515
  • 4
  • 14
-2

you might find some help with these links:

Getting file and line numbers without deploying the PDB files also found this following post

"Hi Mark,

The following will give you the line number of your code (in the source file):

Dim CurrentStack As System.Diagnostics.StackTrace
MsgBox (CurrentStack.GetFrame(0).GetFileLineNumber)

In case you're interested, you can find out about the routine that you're in, as well as all its callers.

Public Function MeAndMyCaller As String
    Dim CurrentStack As New System.Diagnostics.StackTrace
    Dim Myself As String = CurrentStack.GetFrame(0).GetMethod.Name
    Dim MyCaller As String = CurrentStack.GetFrame(1).GetMethod.Name
    Return "In " & Myself & vbCrLf & "Called by " & MyCaller
End Function

This can be very handy if you want a generalised error routine because it can get the name of the caller (which would be where the error occurred).

Regards, Fergus MVP [Windows Start button, Shutdown dialogue] "

Nate Cook
  • 8,395
  • 5
  • 46
  • 37
Richard
  • 1,804
  • 16
  • 22
  • 3
    Unfortunately, StackTrace provides information about the current calling stack; and not about an arbitrary outside member. I would like to get the information without actually running the method. – Yann Trevin Sep 30 '08 at 07:45
  • Richard, the busycode link is broken. Is there an updated link anywhere? – Nate Cook Jun 19 '13 at 23:45