I would like to write a reusable function I can call within any method to log a snapshot of all the local variables. For example:
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);
}
I would like to get a console output like this:
step 1
Int32 a = 1
String s = something
step 2
Int32 a = 2
String s = something
String t = somethingelse
I want to avoid providing a specific list of local variable names.
The closest I could find was MethodInfo.GetCurrentMethod().GetMethodBody().LocalVariables
, but I do not know how to access the values of the local variables using reflection.
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);
}
}
The reflection API seems well suited for static analysis, but not for dynamic analysis like this. For instance, the variable t
is not in scope during the first call to dumpLocalVariables
, but it still appears in the LocalVariables
property of the MethodBody
.
I suspect there is a debugging API that I am overlooking. How does Developer Studio populate the "locals" tab when at a breakpoint? Is there a way to do something similar at runtime?
EDIT:
I can see in ILSpy that my example class uses IL codes like ldloc.0 and ldloc.1 to get to the first and second local variable.
.locals init (
[0] int32 a
[1] string s
[2] string t
)
and later
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
Maybe I could use some sort of proxy-like mechanism that lets me do the same thing? I don't mind if the call to my reusable method is messy, I just want something I can paste into any code block without a lot of hand-editing.
See Question&Answers more detail:
os