【问题标题】:How to set a breakpoint in a lambda expression?如何在 lambda 表达式中设置断点?
【发布时间】:2017-03-15 01:41:30
【问题描述】:

我想调试在表达式树中调用的 lambda。不幸的是,断点从未命中。

这是一个完整的控制台程序:

private static void Main()
{
    var evalAndWrite = EvalAndWrite(x => x + 1 /* a breakpoint here is never hit */);
    evalAndWrite(1);
    Console.ReadLine();
}

private static Action<int> EvalAndWrite(Expression<Func<int, int>> expr)
{
    var result = Expression.Variable(typeof(int), "result");
    var assign = Expression.Assign(result, expr.Body);
    var writeLine = Expression.Call(typeof(Console), nameof(Console.WriteLine), null, result);
    var body = Expression.Block(new[] {result}, assign, writeLine);
    return Expression.Lambda<Action<int>>(body, expr.Parameters[0]).Compile();
}

如果我在 lambda 中设置断点 (即使用 F9 在x + 1 处),则整行会在实际执行时中断,但不会在 lambda 处中断。

body的调试视图我看到了:

.Block(System.Int32 $result) {
    $result = $x + 1;
    .Call System.Console.WriteLine($result)
}

表示复制语义:lambda 的逻辑已“内联”,我想与原始 lambda 的连接丢失了。或者是否有任何技巧可以在 Visual Studio 中调试原始 lambda?

【问题讨论】:

  • 由于您的新 lambda 不直接调用原始 lambda,我不明白您如何在一个将转移到另一个的断点上放置一个断点。

标签: c# linq-expressions


【解决方案1】:

Expression 是数据,而不是代码。通过调用Compile(),它可以变成代码,但在你这样做之前,它不是代码。是数据。而且调试器只能在代码中设置断点。

更具体地说,调试器使用编译时生成的 .pdb 文件中的信息,将编译后的代码与原始源代码相关联。当您使用 lambda 定义 Expression 时,此时您不会生成任何已编译的代码,因此 .pdb 中没有任何内容可以将调试器指向您的 lambda 中的代码。您正在生成一个表达式树,这是一种可以在以后在运行时转换为代码的数据。

(并不是说调试器理论上不可能支持这一点,但据我所知,这需要大量额外的工作,尚未完成的工作。)

根据您的最终目标,您可以通过添加一个间接级别来实现您想要的。例如:

private static void Main()
{
    Func<int, int> e = x => x + 1; // set breakpoint here

    var evalAndWrite = EvalAndWrite(x => e(x));
    evalAndWrite(1);
    Console.ReadLine();
}

当然,这种方法会对EvalAndWrite() 方法隐藏实际的表达式主体。如果您使用表达式作为“反编译”原始 lambda 的一种方式,以便您可以检查它或出于某种原因使用主体的各个部分,那么上述内容将没有用。但是在您的示例中,您似乎没有这样做。所以也许这足以满足您的需求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-22
    • 1970-01-01
    • 2015-08-20
    • 2019-07-20
    相关资源
    最近更新 更多