如何在lambda表达式中设置断点?

Dej*_*jan 5 c# linq-expressions

我想调试在表达式树中调用的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();
}
Run Code Online (Sandbox Code Playgroud)

如果我 lambda中设置一个断点(即x + 1使用F9),整条线在实际执行时会被拉断而不是lambda.

看看body我看到的调试视图:

.Block(System.Int32 $result) {
    $result = $x + 1;
    .Call System.Console.WriteLine($result)
}
Run Code Online (Sandbox Code Playgroud)

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

Pet*_*iho 8

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

当然,这种方法将隐藏方法中的实际表达式主体EvalAndWrite().如果您使用表达式作为"反编译"原始lambda的方式,以便您可以检查它或以其他方式使用身体的各个部分出于某种原因,那么上述内容将没有用处.但在你的例子中,你似乎并没有这样做.所以这可能足以满足您的需求.