2011-05-08 9 views
5

Me gustaría escribir una función reutilizable a la que pueda llamar dentro de cualquier método para registrar una instantánea de todas las variables locales. Por ejemplo:C# función reutilizable para volcar el valor actual de las variables locales

void somemethod() 
    { 
     int a = 1; 
     string s = "something"; 
     dumpLocalVariables("step 1", MethodInfo.GetCurrentMethod(), this); 

     a++; 
     string t = s + "else"; 
     dumpLocalVariables("step 2", MethodInfo.GetCurrentMethod(), this); 
    } 

me gustaría obtener una salida de la consola como esto:

step 1 
    Int32 a = 1 
    String s = something 
step 2 
    Int32 a = 2 
    String s = something 
    String t = somethingelse 

Quiero evitar proporcionar una lista específica de nombres de variables locales.

Lo más parecido que pude encontrar fue MethodInfo.GetCurrentMethod().GetMethodBody().LocalVariables, pero no sé cómo acceder a los valores de las variables locales utilizando la reflexión.

void dumpLocalVariables(string context, MethodBase currentMethod, object obj) 
{ 
    Console.WriteLine(context); 
    MethodBody methodBody = currentMethod.GetMethodBody(); 
    foreach (LocalVariableInfo lvi in methodBody.LocalVariables) 
    { 
     string variableType = lvi.LocalType.Name; 
     // how do I get this? 
     string variableName = "variableNameHere"; 
     // how do I get this?  
     string variableValue = "variableValueHere"; 
     Console.WriteLine(" " + variableType + " " + variableName + 
      " = " + variableValue); 
    } 
} 

La API de reflexión parece adecuada para el análisis estático, pero no para un análisis dinámico como este. Por ejemplo, la variable t no está dentro del alcance durante la primera llamada al dumpLocalVariables, pero aún aparece en la propiedad LocalVariables del MethodBody.

Sospecho que hay una API de depuración que estoy pasando por alto. ¿Cómo completa Developer Studio la pestaña "locales" cuando está en un punto de interrupción? ¿Hay alguna manera de hacer algo similar en tiempo de ejecución?

EDIT:

que puedo ver en mi clase que ILSpy ejemplo utiliza códigos IL como ldloc.0 y ldloc.1 para llegar a la primera y segunda variable local.

.locals init (
    [0] int32 a 
    [1] string s 
    [2] string t 
) 

y más tarde

IL_001b: ldloc.0 // this is a 
IL_001c: ldc.i4.1 
IL_001d: add 
IL_001e: stloc.0 
IL_001f: ldloc.1 // this is s 
IL_0020: ldstr "else" 
IL_0025: call string string::Concat(string, string) 
IL_002a: stloc.2 // this is t 

Tal vez podría utilizar algún tipo de mecanismo de poder-como me permite hacer lo mismo? No me importa si la llamada a mi método reutilizable es desordenada, solo quiero algo que pueda pegar en cualquier bloque de código sin mucha edición manual.

+1

"Sospecho que hay una API de depuración que se me escapa": 'System.Diagnostics.Debugger' –

+0

@Henk: ¿se puede aclarar que ? No sabía que la clase 'Debugger' tenía nada útil que no fuera' IsAttached' y 'Break()'. – Groo

+1

Posibles duplicados: [¿Hay alguna manera de examinar las variables de pila en tiempo de ejecución en C#?] (Http://stackoverflow.com/questions/5130414/is-there-a-way-to-examine-the-stack-variables -at-runtime-in-c), [C# Cómo volcar todas las variables y valores actuales durante el tiempo de ejecución] (http://stackoverflow.com/questions/1552478/c-how-to-dump-all-variables-current- valores durante el tiempo de ejecución). – Groo

Respuesta

7

Ver esta pregunta relacionada:

Is there a simple way to obtain all the local variables in the current stack frame in C# (or CIL)

La respuesta corta es: no se pueden obtener los valores de las variables locales porque están asignados directamente en la pila en tiempo de ejecución, y por lo tanto no está disponible a través de la reflexión. La única forma de hacerlo es a través de la API del depurador ... y está lejos de ser trivial. Además, esto solo funcionaría si su depurador personalizado está realmente conectado al proceso.

Una opción mejor y más factible podría ser a través del ensamblaje. Dijiste que no querías tener el método para saber los nombres específicos de las variables locales para acceder al registrar sus valores. Yo sugeriría la creación de dos métodos:

static void LogVariables(); 

y

static void LogVariables(params string[] names, params object[] values); 

Agregar una tarea posterior a la construcción que llama a una rutina de ensamblaje de tejido que se intercambia a cabo las primeras LogVariables llamada con el segundo, pero proporcionando explícitamente los nombres de las variables/valores al método. Puede escribir esta rutina para modificar el ensamblaje utilizando Mono Cecil (también hay otras herramientas que pueden hacer esto).

http://www.mono-project.com/Cecil

+0

Esta es la respuesta más fácil hasta ahora, pero no es lo que estaba buscando. Todavía me gustaría encontrar algo que pueda insertarse y compilarse de la manera "normal". – hsmiths

+0

Supongo que ya no hay más ideas, por lo que estoy aceptando esta respuesta a regañadientes. Creo que al menos es posible escribir algo utilizando el ensamblado cecil en tiempo de ejecución para realizar este tipo de registro sin la necesidad de un ajuste posterior a la compilación. – hsmiths

+0

si es posible obtener una lista de variables locales ¿por qué no es posible generar código dinámico por Reflection.Emit para volcarlo valores? – devi

2

Es posible mediante el uso de depurador externo para el código administrado. Consulte "ejemplo del depurador gestionado" para saber cómo se hace: http://blogs.msdn.com/b/jmstall/archive/2004/09/30/236281.aspx (incluye el enlace al ejemplo y más información)

+0

Gracias por el enlace. El material de mdbg es una lectura interesante. Tomará mucho más trabajo por adelantado, pero sospecho que puedo cocinar algo basado en esto para hacer lo que quiero. No creo que esté solo en reconocer el patrón de tener que abandonar locales y tener que hacer una única vez cada vez. – hsmiths

Cuestiones relacionadas