5

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

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

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

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

我想避免提供特定的局部变量名称列表。

我能找到的最接近的是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);
    }
}

反射 API 似乎很适合静态分析,但不适合像这样的动态分析。例如,变量t在第一次调用时不在范围内dumpLocalVariables,但它仍然出现LocalVariablesMethodBody.

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

编辑:

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

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

然后

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

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

4

3 回答 3

8

请参阅此相关问题:

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

简短的回答是:您无法获取局部变量的值,因为它们在运行时直接分配在堆栈上,因此无法通过反射获得。做到这一点的唯一方法是通过调试器 API……这绝非易事。此外,这仅在您的自定义调试器实际附加到进程时才有效。

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

static void LogVariables();

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

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

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

于 2011-05-08T18:47:07.930 回答
2

可以通过对托管代码使用外部调试器。请参阅“托管调试器示例”了解它是如何完成的:http: //blogs.msdn.com/b/jmstall/archive/2004/09/30/236281.aspx(包括指向示例的链接和更多信息)

于 2011-05-08T18:51:01.443 回答
0

考虑使用自定义 PostSharp 方面(使用 IL 转换)编写小型转储

一个共享的调试引擎库,用 C# 编写。在 NuGet 上作为 Microsoft.Samples.Debugging.MdbgEngine 可用。

PostSharp 方面的代码作为PADRE(可插入自动调试和报告引擎)存储库的一部分在 GitHub 上提供

于 2012-12-02T03:10:21.183 回答