Visual Studio 调试“快速观察”工具和 lambda 表达式

Posted

技术标签:

【中文标题】Visual Studio 调试“快速观察”工具和 lambda 表达式【英文标题】:Visual Studio debugging "quick watch" tool and lambda expressions 【发布时间】:2010-10-18 01:14:13 【问题描述】:

为什么在“快速观察”窗口中调试时不能使用 lambda 表达式?

UPD:另见

http://blogs.msdn.com/b/jaredpar/archive/2009/08/26/why-no-linq-in-debugger-windows.aspx

http://blogs.msdn.com/b/jaredpar/archive/2010/06/02/why-is-linq-absent-from-debugger-windows-part-2.aspx

【问题讨论】:

这已经完成,在 VS 2015 预览版中可用。 visualstudio.uservoice.com/forums/121579-visual-studio/… gigi.nullneuron.net/gigilabs/… 我尝试了 MSDN 上给出的非常简单的 lambda 表达式示例,但它不起作用。我有 VS 2015 企业版 @Franciscod'Anconia 要在调试中启用 lambda 支持,必须选中“使用托管兼容模式”(***.com/a/36559817/818321)因此,您将无法使用条件断点:@ 987654326@和***.com/a/35983978/818321 【参考方案1】:

不,您不能在监视/本地/即时窗口中使用 lambda 表达式。正如 Marc 所指出的,这非常复杂。不过,我想更深入地探讨这个话题。

大多数人在调试器中执行匿名函数时不考虑的是它不会发生在真空中。定义和运行匿名函数的行为改变了代码库的底层结构。一般来说,更改代码,尤其是从即时窗口更改代码,是一项非常困难的任务。

考虑以下代码。

void Example() 
  var v1 = 42;
  var v2 = 56; 
  Func<int> func1 = () => v1;
  System.Diagnostics.Debugger.Break();
  var v3 = v1 + v2;

此特定代码创建一个闭包来捕获值 v1。每当匿名函数使用在其范围之外声明的变量时,都需要闭包捕获。出于所有意图和目的,此函数中不再存在 v1。最后一行实际上看起来更像以下内容

var v3 = closure1.v1 + v2;

如果函数 Example 在调试器中运行,它将在 Break 行处停止。现在想象一下,如果用户在监视窗口中输入以下内容

(Func<int>)(() => v2);

为了正确执行此操作,调试器(或更合适的 EE)需要为变量 v2 创建一个闭包。这很困难,但并非不可能做到。

对于 EE 来说,真正让这成为一项艰巨的工作的是最后一行。现在应该如何执行该行?出于所有意图和目的,匿名函数删除了 v2 变量并将其替换为closure2.v2。所以最后一行代码现在真的需要阅读

var v3 = closure1.v1 + closure2.v2;

然而,要在代码中实际获得这种效果,需要 EE 更改代码的最后一行,这实际上是一个 ENC 操作。虽然这个特定的例子是可能的,但大部分场景是不可能的。

更糟糕的是执行 lambda 表达式不应该创建一个新的闭包。它实际上应该将数据附加到原始闭包中。在这一点上,您直接进入限制 ENC。

不幸的是,我的小例子只触及了我们遇到的问题的表面。我一直说我会写一篇关于这个主题的完整博客文章,希望这个周末我有时间。

【讨论】:

呜呜呜,接受平庸,呜呜呜呜。调试器是 IDE 的核心,而您却弄坏了它!监视窗口中的 Lambda 不需要捕获任何内容。与任何其他监视代码一样,它们仅在特定的堆栈帧上才有意义。 (或者你捕获变量,移动到另一个具有相同变量名的函数......以及什么?)调试器旨在破解编译器。让它发挥作用! 为什么他们不允许在监视窗口的 lambdas 上捕获变量。简单,并且允许在真正的函数代码中使用 lambdas 的大量调试场景。 @LuizFelipe 即使这仍然是一个大量正在接受。它需要 EE 为回调实际生成完整的函数体(一直到 IL)。今天的 EE 没有做任何此类事情,而是一个解释器。 @JaredPar 你能分享一下marc谈论的博文吗【参考方案2】:

Lambda 表达式,就像匿名方法一样,实际上是非常复杂的野兽。即使我们排除 Expression (.NET 3.5),仍然会留下很多复杂性,尤其是捕获的变量,这些变量从根本上重新构建了使用它们的代码(您的想法因为变量成为编译器生成的类上的字段),有点烟雾和镜子。

因此,我一点也不惊讶您不能无所事事地使用它们 - 有很多编译器工作(以及幕后的类型生成)支持这种魔力。

【讨论】:

【参考方案3】:

您不能在“立即”或“监视”窗口中使用 lambda 表达式。

但是,您可以使用 System.Linq.Dynamic expressions,其形式为 .Where("Id = @0", 2) - 它没有标准 Linq 中可用的全部方法,也没有完整的lambda 表达式的强大功能,但总比没有好!

【讨论】:

嗯......虽然其他人解释说这是不可能的,但这至少为我们提供了一个可能的解决方案。 +1 澄清一下,你“导入 System.Linq.Dynamic”,然后在调试窗口中写下 '"Where(something.AsQueryable,"property>xyz",nothing)' 这很棒。即使您没有获得完整的 Linq 扩展方法,例如没有.Any(string predicate),您可以在Watch Window 中输入.Where("Id&gt;2").Any() 或Pin to Source。太棒了!【参考方案4】:

未来已经到来!

Support for debugging lambda expressions has been added to Visual Studio 2015(在撰写本文时预览)。

必须重写表达式评估器,因此缺少许多功能:远程调试 ASP.NET、在即时窗口中声明变量、检查动态变量等。目前不支持需要调用本机函数的 lambda 表达式。

【讨论】:

很高兴看到。酷...! You can't use this feature with Managed Compatibility Mode or legacy C#/VB expression evaluators【参考方案5】:

这可能会有所帮助: Visual Studio 的扩展即时窗口(在调试中使用 Linq、Lambda Expr)

http://extendedimmediatewin.codeplex.com/ http://dvuyka.spaces.live.com/blog/cns!305B02907E9BE19A!381.entry

一切顺利, 帕特里克

【讨论】:

请注意,虽然第一个链接看起来很棒,但它处于 alpha 阶段,不太可能出现(最后更新于 2008 年)。【参考方案6】:

调试器的表达式求值器不支持 Lambda 表达式...这不足为奇,因为在编译时它们用于创建方法(或表达式树)而不是表达式(看看 Reflector 中的显示切换为 . NET 2 来查看它们)。

当然,它们可以形成一个闭包,另一个完整的结构层。

【讨论】:

好吧,他们可能创建方法;他们可能会创建 Expression 树 - 这取决于上下文。【参考方案7】:

在 VS 2015 中您现在可以这样做,这是他们添加的新功能之一。

【讨论】:

【参考方案8】:

如果您仍需要使用 Visual Studio 2013,您实际上可以使用包管理器控制台窗口在即时窗口中编写循环或 lambda 表达式。就我而言,我在函数顶部添加了一个列表:

private void RemoveRoleHierarchy()

    #if DEBUG
    var departments = _unitOfWork.DepartmentRepository.GetAll().ToList();
    var roleHierarchies = _unitOfWork.RoleHierarchyRepository.GetAll().ToList();
    #endif

    try
    
        //RoleHierarchy
        foreach (SchoolBo.RoleHierarchy item in _listSoRoleHierarchy.Where(r => r.BusinessKeyMatched == false))
            _unitOfWork.RoleHierarchyRepository.Remove(item.Id);

        _unitOfWork.Save();
    
    catch (Exception e)
    
        Debug.WriteLine(e.ToString());
        throw;
    

我的GetAll() 函数在哪里:

private DbSet<T> _dbSet;

public virtual IList<T> GetAll()

    List<T> list;
    IQueryable<T> dbQuery = _dbSet;
    list = dbQuery
        .ToList<T>();

    return list;

在这里我不断收到以下错误,所以我想打印出各个存储库中的所有项目:

InnerException "DELETE 语句与 REFERENCE 约束 \"FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId\" 冲突。冲突发生在数据库 \"CC_Portal_SchoolObjectModel\"、表 \"dbo.Department\"、列 'OranizationalRoleId' 中。\ r\n语句已终止。" System.Exception System.Data.SqlClient.SqlException

然后,我通过在即时窗口中执行此命令来了解部门存储库中有多少条记录:

_unitOfWork.DepartmentRepository.GetAll().ToList().Count

返回 243。

因此,如果您在包管理器控制台中执行以下操作,它会打印出所有项目:

PM> for($i = 0; $i -lt 243; $i++)  $a = $dte.Debugger.GetExpression("departments[$i].OrgagnizationalRoleId"); Write-Host $a.Value $i 

这个想法的作者可以找到here

【讨论】:

【参考方案9】:

为了回答您的问题,这里是 Visual Studio 项目经理对您为什么不能这样做的官方解释。简而言之,因为在 VS 中实现“真的,真的很难”。但该功能目前正在进行中(2014 年 8 月更新)。

Allow the evaluation of lambda expressions while debugging

当你在那里时添加你的投票!

【讨论】:

以上是关于Visual Studio 调试“快速观察”工具和 lambda 表达式的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2015 诊断工具不支持当前调试配置

Visual Studio工具栏中无法选择调试设备

如何使用 Visual Studio 的(Python 工具)调试远程 Python 应用程序?

Visual Studio 禁用诊断工具

Visual Studio 调试工具提示 - 隐藏变量

为啥我在 Visual Studio 2008 的工具下没有调试选项?