Visual Studiodebugging“快速观察”工具和lambdaexpression式

为什么在“快速监视”窗口中debugging时不能使用lambdaexpression式?

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

像匿名方法一样,Lambdaexpression式实际上是非常复杂的动物。 即使我们排除Expression (.NET 3.5),仍然留下很多复杂性,尤其是被捕获的variables,这从根本上重构了使用它们的代码(你认为variables变成编译器生成的类的字段),有点烟雾和镜子。

因此,我毫不意外的是,你不能无用地使用它们 – 编译器的工作量很大 ,而且在后台生成types支持这种魔法。

不,你不能在watch / locals / immediate窗口中使用lambdaexpression式。 正如马克所指出的,这非常复杂。 但是我想进一步深入这个话题。

在debugging器中执行一个匿名函数的时候,大多数人不会考虑的是它不会在真空中发生。 定义和运行匿名函数的行为改变了代码库的底层结构。 一般而言,更改代码,特别是从直接窗口更改代码是一项非常困难的任务。

考虑下面的代码。

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

这个特定的代码创build一个闭包来捕获值v1。 只要匿名函数使用在其作用域之外声明的variables,就需要closures捕获。 对于所有的意图和目的v1不再存在于这个函数中。 最后一行看起来更像以下内容

 var v3 = closure1.v1 + v2; 

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

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

为了正确执行此操作,debugging器(或更适合的EE)需要为variablesv2创build一个闭包。 这是困难的,但并非不可能。

尽pipe这是EE的一个艰难的工作,但是最后一条线。 现在应该如何执行该行? 对于所有的意图和目的,匿名函数删除了v2variables并将其replace为closure2.v2。 所以现在最后一行代码需要阅读

 var v3 = closure1.v1 + closure2.v2; 

然而,为了在代码中实际获得这种效果,需要EE改变实际上是ENC动作的最后一行代码。 虽然这个具体的例子是可能的,但很大一部分情况并不是。

更糟糕的是执行lambdaexpression式不应该创build一个新的闭包。 它实际上应该是将数据附加到原始的closures。 此时您直接进入限制ENC。

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

在“立即”或“监视”窗口中不能使用lambdaexpression式。

但是,您可以使用System.Linq.Dynamicexpression式 ,其forms为.Where(“Id = @ 0”,2) – 它没有标准Linq中可用的全部方法,并且没有满lambdaexpression式的力量,但仍然比没有好!

未来已经到来!

支持debugginglambdaexpression式已被添加到Visual Studio 2015 (在撰写本文时预览 )。

Expression Evaluator必须被重写,所以缺less很多function:远程debuggingASP.NET,在立即窗口中声明variables,检查dynamicvariables等。此外,当前不支持调用本地函数的lambdaexpression式。

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

一切顺利,帕特里克

Lambdaexpression式不被debugging器的expression式求值器所支持…这并不奇怪,因为在编译时它们被用来创build方法(或expression式树)而不是expression式(在显示切换到.NET 2的Reflector中查看看他们)。

当然,他们还可以形成一个封闭的结构。

在VS 2015中,你现在可以做到这一点,这是他们添加的新function之一。

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

 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}

然后,通过在即时窗口中执行此操作,我发现部门存储库中有多less条logging:

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

其中返回243。

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

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

这个想法的作者可以在这里find

要回答你的问题,这里是Visual Studio程序pipe理器官方解释为什么你不能这样做。 总之,因为在VS中实现“真的很难”。 但function目前正在进行(2014年8月更新)。

允许在debugging时评估lambdaexpression式

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