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

标签 c# debugging reflection local-variables code-reuse

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

    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 时不在范围内,但它仍然出现在 LocalVariables 属性中>方法主体

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

编辑:

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

.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

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

最佳答案

请参阅此相关问题:

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

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

更好、更可行的选择可能是通过组装编织。你说你不想让方法在记录它们的值时必须知道要访问的局部变量的具体名称。我建议创建两种方法:

static void LogVariables();

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

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

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

关于C# 可重用函数转储局部变量的当前值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5929384/

相关文章:

c# - 如何获取条件为字符串数组的记录

c# - 如何使用字符串参数来区分 namespace 或类型?

java - 康威的生命游戏——细胞在不该死的时候死了? ( java )

c# - 什么是 Azure 函数 Cron Expression 在每个月的特定日期触发?

c# - 在 Blazor 应用程序中使用 Azure Active Directory 时如何在 asp net core 3.1 中自定义注销页面

c++ - 在 Visual Studio 2015 中使用 QtAddIn 调试 Visual Studio 2013 运行时

c++ - 在 Valgrind 中运行我的程序时如何调试?

java如何找到我不知名的兄弟? (2 个实现相同接口(interface)的类)

c# - C#中如何判断集合中对象的类型

java - 如何判断反射得到的Java字段的值是否为null?