Как получить имя исходного файла и номер строки члена типа?

Учитывая, что файл данных отладки доступен (PDB) и с помощью System.Reflection или другой подобной структуры, такой как Mono.Cecil, как получить программную имя исходного файла и номер строки, в которой объявлен тип или член типа.

Например, скажем, вы скомпилировали этот файл в сборку:

C:\MyProject\Foo.cs

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

Как сделать что-то вроде:

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

sourceFileName будет содержать "C:\MyProject\Foo.cs", а sourceLineNumber будет равно 3.

Обновление: System.Diagnostics.StackFrame действительно может получить эту информацию, но только в области текущего исполняемого стека вызовов. Это означает, что сначала нужно вызвать метод. Я хотел бы получить ту же информацию, но не вызывая член типа.

Ответы

Ответ 1

Используя считыватель PDB, предоставленный Project метаданных CCI, можно извлечь местоположение кода для данного члена типа. См. Пример реализации в исходном коде OSS Gallio Project.

Ответ 2

Современный метод:

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);
}

Новый Framework API, который заполняет аргументы (отмеченные специальными атрибутами) во время выполнения, см. больше в мой ответ на этот вопрос SO

Ответ 3

вы можете найти помощь по этим ссылкам:

Получение номеров файлов и строк без развертывания файлов PDB  также нашел следующее post

"Привет, Марк,

Следующее предоставит вам номер строки вашего кода (в исходный файл):

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

Если вам интересно, вы можете узнать о том, что вы в, а также всех его вызывающих абонентов.

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

Это может быть очень удобно, если вы хотите использовать генерализованную ошибку, поскольку она может получить имя вызывающего абонента (которое было бы там, где произошла ошибка).

С уважением, Фергус MVP [Кнопка запуска Windows, диалог завершения работы] "

Ответ 4

Используя один из методов, описанных выше, внутри конструктора атрибута вы можете предоставить исходное местоположение всего, что может иметь атрибут - например, класс. См. Следующий класс атрибутов:

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
{
   ...
}

Вы можете написать, например:

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

Выход будет:

a:\develop\HWClassLibrary.cs\src\Tester\Program.cs(65):