繁体   English   中英

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

[英]Visual Studio debugging “quick watch” tool and lambda expressions

为什么在“快速监视”窗口中调试时不能使用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

不,您不能在watch / locals /即时窗口中使用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;

如果在调试器中运行示例函数,它将在中断行处停止。 现在,假设用户是否在监视窗口中输入了以下内容

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

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

对于EE而言,真正使这项工作变得艰难的是最后一行。 现在应该如何执行该行? 出于所有目的和目的,匿名函数删除了v2变量,并将其替换为closure2.v2。 所以现在实际上需要阅读最后一行代码

var v3 = closure1.v1 + closure2.v2;

然而,要在代码中实际获得这种效果,则EE必须更改最后一行代码,这实际上是ENC动作。 尽管可以使用此特定示例,但大部分情况都不可行。

更糟糕的是执行lambda表达式不应创建新的闭包。 它实际上应该将数据附加到原始闭包中。 此时,您会直接遇到ENC的限制。

不幸的是,我的小例子只能解决我们遇到的问题。 我一直说我会写一篇完整的博客文章,希望这个周末有时间。

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

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

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

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

未来来了!

对调试lambda表达式的支持已添加到Visual Studio 2015 (在撰写本文时为预览 )。

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

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

一切顺利,帕特里克

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

加上它们当然可以形成闭合,这是另一整个结构层。

为了回答您的问题,这是Visual Studio程序管理器对为什么您不能执行此操作的正式解释。 简而言之,因为在VS中实现“确实非常非常困难”。 但是该功能目前正在开发中(2014年8月更新)。

调试时允许评估lambda表达式

在那里就添加投票!

如果仍然需要使用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 \\ nThen语句已终止。“} 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 }

这个想法的作者可以在这里找到

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM