C#可重用函数转储局部变量的当前值

hsm*_*ths 5 c# reflection debugging code-reuse local-variables

我想编写一个可重用的函数,我可以在任何方法中调用它来记录所有局部变量的快照.例如:

    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);
    }
Run Code Online (Sandbox Code Playgroud)

我想获得这样的控制台输出:

step 1
    Int32 a = 1 
    String s = something
step 2
    Int32 a = 2
    String s = something
    String t = somethingelse
Run Code Online (Sandbox Code Playgroud)

我想避免提供一个特定的局部变量名列表.

我能找到的最接近的是MethodInfo.GetCurrentMethod().GetMethodBody().LocalVariables,但我不知道如何使用反射访问局部变量的值.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

反射API似乎非常适合静态分析,但不适用于这样的动态分析.例如,变量t在第一次调用期间不在范围内dumpLocalVariables,但它仍然出现在该LocalVariables属性中MethodBody.

我怀疑有一个我忽略的调试API.Developer Studio如何在断点处填充"本地人"选项卡?有没有办法在运行时做类似的事情?

编辑:

我可以在ILSpy中看到我的示例类使用IL代码(如ldloc.0和ldloc.1)来获取第一个和第二个局部变量.

.locals init (
    [0] int32 a
    [1] string s
    [2] string t
)
Run Code Online (Sandbox Code Playgroud)

然后

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
Run Code Online (Sandbox Code Playgroud)

也许我可以使用某种类似代理的机制让我做同样的事情?我不介意对我的可重用方法的调用是否混乱,我只想要一些我可以粘贴到任何代码块而无需大量手工编辑的东西.

Jef*_*eff 8

看到这个相关的问题:

有没有一种简单的方法来获取C#(或CIL)中当前堆栈帧中的所有局部变量

简短的回答是:您无法获取局部变量的值,因为它们在运行时直接在堆栈上分配,因此无法通过反射获得.执行此操作的唯一方法是通过调试器API ...而且它远非微不足道.此外,这仅在您的自定义调试器实际附加到进程时才有效.

更好,更可行的选择可能是通过装配编织.您说您不希望方法必须知道在记录其值时要访问的本地变量的特定名称.我建议创建两种方法:

static void LogVariables();
Run Code Online (Sandbox Code Playgroud)

static void LogVariables(params string[] names, params object[] values);
Run Code Online (Sandbox Code Playgroud)

添加一个post build任务,该任务调用一个程序集编织例程,该例程将第一个LogVariables调用与第二个进行交换,但显式地为该方法提供变量名称/值.您可以编写此例程来使用Mono Cecil修改程序集(还有其他工具可以执行此操作).

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