Jak uzyskać aktualny numer linii?

117

Oto przykład tego, co chcę zrobić:

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

W powyższym kodzie CurrentLineNumber, powinno być numerem linii w kodzie źródłowym tego fragmentu kodu.

Jak mogę to zrobić?

MonsterMMORPG
źródło
Nie możesz tego niezawodnie zrobić, ponieważ kompilator JIT może dokonać optymalizacji (np. Kodu wbudowanego), co oznacza, że ​​numery linii będą nieprawidłowe.
adrianbanks,
1
Ponieważ możesz wyłączyć optymalizację, jeśli chcesz, możesz to niezawodnie zrobić.
jwg

Odpowiedzi:

175

W .NET 4.5 / C # 5 możesz sprawić, by kompilator wykonał tę pracę za Ciebie, pisząc metodę narzędzia, która używa nowych atrybutów obiektu wywołującego:

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

Spowoduje to wyświetlenie na przykład:

Boo w linii 39 (SomeMethodSomewhere)

Jest tam również, [CallerFilePath]który podaje ścieżkę do oryginalnego pliku kodu.

Marc Gravell
źródło
bardzo dziękuję za odpowiedź. czy można nauczyć się także nazwy obiektu? och, pomyliłem się z czymś innym. zastanawiam się nad stroną internetową asp.net 4.5. globalny łapacz błędów. złapać błąd spowodowany nazwą obiektu?
MonsterMMORPG,
@MonsterMMORPG nope; tylko 3, o których wspomniałem powyżej
Marc Gravell
1
@MarcGravell Czy to wymaga, aby środowisko wykonawcze miało również 4,5, CZY jest to funkcja kompilatora?
kuldeep
5
C # jest tak dobrze zaprojektowany. Nigdy nie przestaje mnie zadziwiać. Dzięki Marc!
nmit026
74

Użyj metody StackFrame.GetFileLineNumber , na przykład:

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

Zobacz wpis na blogu Scotta Hanselmana na aby uzyskać więcej informacji.

[Edycja: dodano następujące]

W przypadku użytkowników korzystających z .Net 4.5 lub nowszego należy rozważyć atrybuty CallerFilePath , CallerMethodName i CallerLineNumber w przestrzeni nazw System.Runtime.CompilerServices. Na przykład:

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

Argumentami muszą być argumenty stringfor CallerMemberNamei CallerFilePathoraz intfor CallerLineNumberi muszą mieć wartość domyślną. Określenie tych atrybutów w parametrach metody instruuje kompilator, aby wstawił odpowiednią wartość do kodu wywołującego w czasie kompilacji, co oznacza, że ​​działa on przez zaciemnianie. Aby uzyskać więcej informacji, zobacz Informacje o dzwoniącym .

akton
źródło
@MonsterMMORPG Działa to niezależnie od tego, czy wystąpił błąd, czy nie. Klasa StackFrame po prostu patrzy na metodę wywołującą wykonywaną metodę. Pierwszym argumentem konstruktora StackFrame jest wywołanie depth (1), a drugi argument wskazuje, że potrzebne są informacje o pliku.
akton
3
Jeśli kompilujesz StackFrameprzykład na Mono , pamiętaj, aby używać go--debug w czasie kompilacji iw czasie wykonywania
bernard paulus
StackFramenie jest dostępny w .NET Core. Skorzystaj z odpowiedzi Marca Gravella.
Jesse Chisholm
Używanie wartości domyślnej = string.Emptygeneruje błąd „Domyślna wartość parametru dla„ CallFilePath ”musi być stałą czasu kompilacji” !
stomia
1
@stomy Zmieniłem examp [le, aby używać podwójnych cudzysłowów ( "") zamiast string.Empty.
akton
21

Wolę jeden wkład, więc:

int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
iambriansreed
źródło
8
potrzebuje pliku .pdb .. którego generalnie nie generujemy / nie kopiujemy na serwer produkcyjny.
Deepak Sharma
4

Dla tych, którzy potrzebują rozwiązania metody .NET 4.0+:

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

Jak zadzwonić:

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

Wynik:

Void Test () (FILENAME.cs: 104)

Popatrz tutaj!

Zmień format Console.WriteLine, jak chcesz!

Jared Burrows
źródło
3
nie zadziała we wszystkich przypadkach .. zawsze potrzebuje pliku .pdb, którego generalnie nie generujemy / kopiujemy na serwer produkcyjny. spróbuj z atrybutem Caller * w języku C # 5.0.
Deepak Sharma
2
Jeśli zamiast tego użyjesz this: System.Diagnostics.Debug.WriteLine(String.Format("{0}({1}): {2}: {3}", fileName, lineNumber, methodName, message));możesz kliknąć linię w oknie wyjściowym i przejść do tej linii w źródle.
Jesse Chisholm
3

Jeśli jest w bloku try catch, użyj tego.

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
źródło
1

W .NET 4.5 możesz uzyskać numer linii, tworząc funkcję:

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

Wtedy za każdym razem, gdy zadzwonisz LineNumber(), będziesz mieć bieżącą linię. Ma to tę przewagę nad jakimkolwiek rozwiązaniem używającym StackTrace, że powinno działać zarówno w debugowaniu, jak i wydaniu.

Biorąc więc pierwotną prośbę o to, co jest wymagane, stałoby się:

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

Opiera się to na doskonałej odpowiedzi Marca Gravella.

Brian Cryer
źródło
To nie zwraca prawidłowego numeru wiersza. Z jakiegoś powodu muszę odjąć 191, żeby zrobić to dobrze.
Daniel,
Ciekawy. U mnie działa dobrze. Czy masz włączone numery linków w IDE? Jeśli wywołasz tę funkcję z różnych miejsc w pliku, czy nadal musisz odjąć 191? Będzie to albo błąd kompilatora (mało prawdopodobny, ale możliwy) lub zwinięty blok na twojej stronie (chociaż nie powinno to uniemożliwić poprawności numerów wierszy, może wyjaśnić różnicę, jeśli liczysz, a nie sprawdzasz numer wiersza). Jeśli możesz skontaktować się ze mną w trybie offline, chciałbym przejść do sedna sprawy.
Brian Cryer,
Żadne zwinięte bloki, numery linii są włączone, nadal trzeba odjąć 191 niezależnie od tego, skąd jest wywołany. Wiem ... dziwne.
Daniel,