简体   繁体   English

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

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

Why can't I use lambda expressions while debugging in “Quick watch” window? 为什么在“快速监视”窗口中调试时不能使用lambda表达式?

UPD: see also 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/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 http://blogs.msdn.com/b/jaredpar/archive/2010/06/02/why-is-linq-absent-from-debugger-windows-part-2.aspx

No you cannot use lambda expressions in the watch / locals / immediate window. 不,您不能在watch / locals /即时窗口中使用lambda表达式。 As Marc has pointed out this is incredibly complex. 正如Marc所指出的那样,这非常复杂。 I wanted to dive a bit further into the topic though. 不过,我想进一步探讨这个话题。

What most people don't consider with executing an anonymous function in the debugger is that it does not occur in a vaccuum. 大多数人在调试器中执行匿名函数时不会考虑的是,它不会在真空中发生。 The very act of defining and running an anonymous function changes the underlying structure of the code base. 定义和运行匿名函数的行为改变了代码库的基础结构。 Changing the code, in general, and in particular from the immediate window, is a very difficult task. 通常,尤其是从即时窗口更改代码是一项非常困难的任务。

Consider the following code. 考虑下面的代码。

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

This particular code creates a single closure to capture the value v1. 此特定代码创建一个单个闭包以捕获值v1。 Closure capture is required whenever an anonymous function uses a variable declared outside it's scope. 每当匿名函数使用在其作用域之外声明的变量时,都需要捕获闭包。 For all intents and purposes v1 no longer exists in this function. 出于所有意图和目的,此功能中不再存在v1。 The last line actually looks more like the following 最后一行实际上更像以下内容

var v3 = closure1.v1 + v2;

If the function Example is run in the debugger it will stop at the Break line. 如果在调试器中运行示例函数,它将在中断行处停止。 Now imagine if the user typed the following into the watch window 现在,假设用户是否在监视窗口中输入了以下内容

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

In order to properly execute this the debugger (or more appropriate the EE) would need to create a closure for variable v2. 为了正确执行此操作,调试器(或更合适的EE)将需要为变量v2创建一个闭包。 This is difficult but not impossible to do. 这很难但并非不可能。

What really makes this a tough job for the EE though is that last line. 对于EE而言,真正使这项工作变得艰难的是最后一行。 How should that line now be executed? 现在应该如何执行该行? For all intents and purposes the anonymous function deleted the v2 variable and replaced it with closure2.v2. 出于所有目的和目的,匿名函数删除了v2变量,并将其替换为closure2.v2。 So the last line of code really now needs to read 所以现在实际上需要阅读最后一行代码

var v3 = closure1.v1 + closure2.v2;

Yet to actually get this effect in code requires the EE to change the last line of code which is actually an ENC action. 然而,要在代码中实际获得这种效果,则EE必须更改最后一行代码,这实际上是ENC动作。 While this specific example is possible, a good portion of the scenarios are not. 尽管可以使用此特定示例,但大部分情况都不可行。

What's even worse is executing that lambda expression shouldn't be creating a new closure. 更糟糕的是执行lambda表达式不应创建新的闭包。 It should actually be appending data to the original closure. 它实际上应该将数据附加到原始闭包中。 At this point you run straight on into the limitations ENC. 此时,您会直接遇到ENC的限制。

My small example unfortunately only scratches the surface of the problems we run into. 不幸的是,我的小例子只能解决我们遇到的问题。 I keep saying I'll write a full blog post on this subject and hopefully I'll have time this weekend. 我一直说我会写一篇完整的博客文章,希望这个周末有时间。

Lambda expressions, like anonymous methods, are actually very complex beasts. Lambda表达式就像匿名方法一样,实际上是非常复杂的野兽。 Even if we rule out Expression (.NET 3.5), that still leaves a lot of complexity, not least being captured variables, which fundamentally re-structure the code that uses them (what you think of as variables become fields on compiler-generated classes), with a bit of smoke and mirrors. 即使我们排除Expression (.NET 3.5),仍然会留下很多复杂性,尤其是被捕获的变量,它们从根本上重新构造了使用它们的代码(您认为变量成为编译器生成的类中的字段) ),并带有一点烟雾和镜子。

As such, I'm not in the least surprised that you can't use them idly - there is a lot of compiler work (and type generation behind the scenes) that supports this magic. 因此,您可以无所事事地使用它们,我一点也不感到惊讶-有很多支持这种魔术的编译器工作(以及幕后的类型生成)。

You can't use lambda expressions in the Immediate or Watch windows. 您不能在“即时”或“监视”窗口中使用lambda表达式。

You can however use System.Linq.Dynamic expressions , which take the form .Where("Id = @0", 2) - it doesn't have the full range of methods available in standard Linq, and doesn't have the full power of lambda expressions, but still, it's better than nothing! 但是,您可以使用System.Linq.Dynamic表达式 ,其形式为.Where(“ Id = @ 0”,2)-它没有标准Linq中可用的全部方法,也没有完整的方法Lambda表达式的强大功能,但总比没有好!

The future has come! 未来来了!

Support for debugging lambda expressions has been added to Visual Studio 2015 ( Preview at the time of writing). 对调试lambda表达式的支持已添加到Visual Studio 2015 (在撰写本文时为预览 )。

Expression Evaluator had to be rewritten, so many features are missing: remote debugging ASP.NET, declaring variables in Immediate window, inspecting dynamic variables etc. Also lambda expressions that require calls to native functions aren't currently supported. Expression Evaluator必须重写,因此缺少许多功能:远程调试ASP.NET,在Instant窗口中声明变量,检查动态变量等。目前还不支持需要调用本机函数的lambda表达式。

this might help: Extended Immediate Window for Visual Studio (use Linq, Lambda Expr in Debugging) 这可能会有所帮助:Visual Studio的扩展即时窗口(在调试中使用Linq,Lambda Expr)

All the best, Patrick 一切顺利,帕特里克

Lambda expressions are not supported by the debugger's expression evaluator... which is hardly surprising since at compile time they are used to create methods (or Expression Trees) rather than expressions (take a look in Reflector with the display switched to .NET 2 to see them). Lambda表达式不受调试器的表达式评估器支持...这不足为奇,因为在编译时,它们用于创建方法(或表达式树)而不是表达式(在Reflector中将显示切换到.NET 2来查看)。看他们)。

Plus of course they could form a closure, another whole layer of structure. 加上它们当然可以形成闭合,这是另一整个结构层。

To answer your question, here's the Visual Studio Program Manager's official explanation of why you can't do this. 为了回答您的问题,这是Visual Studio程序管理器对为什么您不能执行此操作的正式解释。 In short, because "it's really, really hard" to implement in VS. 简而言之,因为在VS中实现“确实非常非常困难”。 But the feature is currently in progress (as updated on Aug 2014). 但是该功能目前正在开发中(2014年8月更新)。

Allow the evaluation of lambda expressions while debugging 调试时允许评估lambda表达式

Add your vote while you're there! 在那里就添加投票!

If you still need to use Visual Studio 2013, you can actually write a loop, or lambda expression in the immediate window using also the package manager console window. 如果仍然需要使用Visual Studio 2013,则实际上也可以使用包管理器控制台窗口在立即窗口中编写循环或lambda表达式。 In my case, I added a list at the top of the function: 就我而言,我在函数顶部添加了一个列表:

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;
    }
}

Where my GetAll() function is: 我的GetAll()函数在哪里:

private DbSet<T> _dbSet;

public virtual IList<T> GetAll()
{
    List<T> list;
    IQueryable<T> dbQuery = _dbSet;
    list = dbQuery
        .ToList<T>();

    return list;
}

Here I kept getting the following error, so I wanted to print out all the items in the various repositories: 在这里,我一直收到以下错误,因此我想打印出各个存储库中的所有项目:

InnerException {"The DELETE statement conflicted with the REFERENCE constraint \\"FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId\\". The conflict occurred in database \\"CC_Portal_SchoolObjectModel\\", table \\"dbo.Department\\", column 'OranizationalRoleId'.\\r\\nThe statement has been terminated."} System.Exception {System.Data.SqlClient.SqlException} InnerException {“ DELETE语句与REFERENCE约束\\” FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \\“发生冲突。冲突发生在数据库\\” CC_Portal_SchoolObjectModel \\“,表\\” dbo.Department \\“,列'OranizationalRoleId'中。\\ r \\ nThen语句已终止。“} System.Exception {System.Data.SqlClient.SqlException}

Then, I find out how many records are in the department repository by executing this in the immediate window: 然后,通过在立即窗口中执行以下操作,找出部门存储库中有多少条记录:

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

Which returned 243. 其中返回243。

So, if you execute the following in the package manager console, it prints out all the items: 因此,如果您在包管理器控制台中执行以下命令,它将打印出所有项目:

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

The author for the idea can be found here 这个想法的作者可以在这里找到

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

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

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