Visual Studio调试“快速监视”工具和lambda表达式

时间:2009-04-07 12:40:27

标签: c# visual-studio debugging lambda

9 个答案:

答案 0 :(得分:91)

不,你不能在watch / locals / immediate窗口中使用lambda表达式。正如马克所指出的那样,这非常复杂。我想进一步深入探讨这个话题。

大多数人在调试器中执行匿名函数时不考虑的是它不会出现在vaccuum中。定义和运行匿名函数的行为改变了代码库的底层结构。通常,特别是从即时窗口更改代码是一项非常困难的任务。

请考虑以下代码。

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。

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

答案 1 :(得分:63)

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

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

答案 2 :(得分:49)

您不能在Immediate或Watch窗口中使用lambda表达式。

然而,您可以使用System.Linq.Dynamic expressions,其形式为。Where(“Id = @ 0”,2) - 它没有标准Linq中可用的全部方法,并且没有lambda表达式的全部功能,但是,它总比没有好!

答案 3 :(得分:21)

未来已经到来!

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

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

答案 4 :(得分:5)

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

一切顺利, 帕特里克

答案 5 :(得分:2)

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

当然,他们可以形成一个封闭,另一整层结构。

答案 6 :(得分:1)

要回答您的问题,请参阅Visual Studio程序管理员的正式解释,说明您为何不能这样做。简而言之,因为&#34;它真的,非常难以&#34;在VS中实现但该功能目前正在进行中(2014年8月更新)。

Allow the evaluation of lambda expressions while debugging

在你那里添加你的投票!

答案 7 :(得分:1)

如果您仍然需要使用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 {&#34; DELETE语句与REFERENCE约束冲突\&#34; FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \&#34;。冲突发生在数据库\&#34; CC_Portal_SchoolObjectModel \&#34;,table \&#34; dbo.Department \&#34;,column&#39; OranizationalRoleId&#39;。\ r \ n该声明已终止。&#34;} 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

答案 8 :(得分:1)

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