¿Cómo obtengo el número de línea actual?

117

Aquí hay un ejemplo de lo que quiero hacer:

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

En el código de arriba CurrentLineNumber, debería estar el número de línea en el código fuente de este fragmento de código.

¿Cómo puedo hacer eso?

MonstruoMMORPG
fuente
No puede hacer esto de manera confiable , ya que el compilador JIT puede realizar optimizaciones (por ejemplo, código en línea), lo que significa que sus números de línea serán incorrectos.
adrianbanks
1
Dado que puede desactivar la optimización si lo desea, puede hacerlo de manera confiable.
jwg

Respuestas:

175

En .NET 4.5 / C # 5, puede hacer que el compilador haga este trabajo por usted escribiendo un método de utilidad que use los nuevos atributos de llamada:

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

Esto mostrará, por ejemplo:

Boo en la línea 39 (SomeMethodSomewhere)

También hay una [CallerFilePath]que le indica la ruta del archivo de código original.

Marc Gravell
fuente
Muchas gracias por la respuesta. ¿Es posible aprender también el nombre del objeto? oh, lo confundí con algo más. lo que me pregunto es el sitio web asp.net 4.5. receptor de errores global. detectar el nombre del objeto causado por el error?
MonsterMMORPG
@MonsterMMORPG no; solo los 3 que mencioné anteriormente
Marc Gravell
1
@MarcGravell, ¿esto requiere que el entorno de tiempo de ejecución también sea 4.5 O es una característica del compilador?
kuldeep
5
C # está tan bien diseñado. Nunca deja de sorprenderme. Gracias Marc!
nmit026
74

Utilice el método StackFrame.GetFileLineNumber , por ejemplo:

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

Consulte la entrada del blog de Scott Hanselman para obtener más información.

[Editar: se agregó lo siguiente]

Para aquellos que usan .Net 4.5 o posterior, considere los atributos CallerFilePath , CallerMethodName y CallerLineNumber en el espacio de nombres System.Runtime.CompilerServices. Por ejemplo:

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

Los argumentos deben ser stringpara CallerMemberNamey CallerFilePathy intpara CallerLineNumbery deben tener un valor predeterminado. La especificación de estos atributos en los parámetros del método indica al compilador que inserte el valor apropiado en el código de llamada en el momento de la compilación, lo que significa que funciona mediante la ofuscación. Consulte Información de la persona que llama para obtener más información.

Akton
fuente
@MonsterMMORPG Esto funciona independientemente de si hay un error o no. La clase StackFrame solo está mirando el método que llama a la corriente que se está ejecutando. El primer argumento del constructor StackFrame es la profundidad de la llamada (1) y el segundo argumento indica que se necesita información de archivo.
Akton
3
Si está compilando el StackFrameejemplo en Mono , asegúrese de usarlo--debug en tiempo de compilación y en tiempo de ejecución
bernard paulus
StackFrameno está disponible en .NET Core. Utilice la respuesta de Marc Gravell.
Jesse Chisholm
El uso del valor predeterminado = string.Emptyarroja el error "El valor del parámetro predeterminado para 'callingFilePath' debe ser una constante en tiempo de compilación" .
stomy
1
@stomy Cambié el ejemplo [archivo para usar comillas dobles ( "") en lugar de string.Empty.
akton
21

Prefiero una línea, así que:

int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
iambriansreed
fuente
8
necesita un archivo .pdb ... que generalmente no generamos / copiamos al servidor de producción.
Deepak Sharma
4

Para aquellos que necesitan una solución de método .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);
}

Como llamar:

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

Salida:

Prueba nula () (FILENAME.cs: 104)

¡Mira aquí!

¡Cambie el formato Console.WriteLine como desee!

Jared Burrows
fuente
3
no funcionará en todos los casos ... siempre necesita un archivo .pdb, que generalmente no generamos / copiamos al servidor de producción. intente con el atributo C # 5.0 Caller *.
Deepak Sharma
2
Si en cambio usa esto: System.Diagnostics.Debug.WriteLine(String.Format("{0}({1}): {2}: {3}", fileName, lineNumber, methodName, message));entonces puede hacer clic en la línea en la ventana de salida y ser llevado a esa línea en la fuente.
Jesse Chisholm
3

Si está en un bloque de captura de prueba, usa esto.

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
fuente
1

En .NET 4.5 puede obtener el número de línea creando la función:

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

Luego, cada vez que llame LineNumber(), tendrá la línea actual. Esto tiene la ventaja sobre cualquier solución que utilice StackTrace de que debería funcionar tanto en depuración como en versión.

Entonces, tomando la solicitud original de lo que se requiere, se convertiría en:

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

Esto se basa en la excelente respuesta de Marc Gravell.

Brian Cryer
fuente
Esto no devuelve el número de línea correcto. Tengo que restar 191 para hacerlo bien por alguna razón.
Daniel
Interesante. Funciona bien aquí para mí. ¿Tiene los números de enlace activados en el IDE? Si llama a esta función desde diferentes lugares del archivo, ¿todavía tiene que restar 191? Esto será un error del compilador (poco probable, pero posible) o un bloque colapsado en su página (aunque eso no debería evitar que los números de línea sean correctos, podría explicar la diferencia si estuviera contando en lugar de buscar el número de línea). Si puedes contactarme sin conexión, me encantaría llegar al fondo.
Brian Cryer
No hay bloques colapsados, los números de línea están activados, aún hay que restar 191 independientemente de dónde se llame. Lo sé ... raro.
Daniel