为什么我不能在“快速监视”窗口中调试时使用lambda表达式?
UPD:另见
http://blogs.msdn.com/b/jaredpar/archive/2009/08/26/why-no-linq-in-debugger-windows.aspx
答案 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中,您现在可以这样做,这是他们添加的新功能之一。