96

Why can't I use lambda expressions while debugging in “Quick watch” window?

UPD: see also

Link

Link

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
lak-b
  • 2,115
  • 4
  • 18
  • 30
  • 5
    This has been completed and is available in VS 2015 preview. http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2049821-debug-lambda-expressions-planned – Francisco d'Anconia Nov 12 '14 at 20:37
  • http://gigi.nullneuron.net/gigilabs/vs2015-preview-debugger-support-for-lambdas-and-linq/ – Pedram Aug 07 '15 at 04:07
  • i tried very simple example given on MSDN for lambda expression but it does not work. i have VS 2015 enterprise edition – Adeem Mar 11 '16 at 08:02
  • 2
    @Franciscod'Anconia to enable lambda support in debug, "Use Managed Compatibility Mode" has to be checked off (https://stackoverflow.com/a/36559817/818321) As a result, you will not be able to use conditional breakpoints: https://blogs.msdn.microsoft.com/devops/2013/10/16/switching-to-managed-compatibility-mode-in-visual-studio-2013/#comment-27136 and https://stackoverflow.com/a/35983978/818321 – Myk Aug 31 '17 at 10:06

9 Answers9

91

No you cannot use lambda expressions in the watch / locals / immediate window. As Marc has pointed out this is incredibly complex. 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. 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. 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. This is difficult but not impossible to do.

What really makes this a tough job for the EE though is that last line. 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. 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. 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. It should actually be appending data to the original closure. At this point you run straight on into the limitations 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.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 43
    Whine, whine, accept mediocrity, whine, whine. The debugger is the heart of the IDE, and you broke it! Lambdas in the watch window don't need to capture anything. Like any other watch code, they make sense only at the particular stack frame. (Or else you capture the variable, move to another function with the same variable name... and what?) The debugger is meant to hack around the compiler. Make it work! – Aleksandr Dubinsky Jun 25 '13 at 09:34
  • 2
    Why they simple don't allow captured variables on lambdas on watch window. Simple and would allow a bunch of debug scenarios where lambdas are just being used in truly functional code. – Luiz Felipe Jan 16 '14 at 18:26
  • @LuizFelipe even that is still a *massive* under taking. It requires the EE to actually generate the full function body for the call back (all the way to IL). The EE does nothing of this sort today, instead it's an interpreter. – JaredPar Jan 16 '14 at 18:29
  • 1
    @JaredPar can you share blog post marc talking about – Ehsan Sajjad Jun 08 '15 at 06:34
64

Lambda expressions, like anonymous methods, are actually very complex beasts. 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.

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
49

You can't use lambda expressions in the Immediate or Watch windows.

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!

stusherwin
  • 1,836
  • 19
  • 19
  • 2
    Well ... while the others explained while it wasn't possible, this one at least provides us with a possible solution. +1 – Nullius Aug 21 '13 at 07:37
  • 1
    Just to clarify, you "Import System.Linq.Dynamic" and then in the debug window you write '"Where(something.AsQueryable,"property>xyz",nothing)' – smirkingman Jun 06 '14 at 13:24
  • This is great. Even though you don't get the full range of Linq Extension methods, e.g. there is no `.Any(string predicate)`, you _can_ put something like: `.Where("Id>2").Any()` in the Watch Window, or Pin to Source. It's great! – Protector one Aug 07 '17 at 11:17
22

The future has come!

Support for debugging lambda expressions has been added to Visual Studio 2015 (Preview at the time of writing).

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.

Athari
  • 33,702
  • 16
  • 105
  • 146
  • That's nice to See. Cool...! – Rahul Nikate Mar 12 '15 at 05:36
  • [You can't use this feature with Managed Compatibility Mode or legacy C#/VB expression evaluators](http://stackoverflow.com/questions/36559399/lambda-expressions-in-immediate-window-for-vs2015) – Chris Apr 14 '17 at 15:21
5

this might help: Extended Immediate Window for Visual Studio (use Linq, Lambda Expr in Debugging)

All the best, Patrick

Patrick Wolf
  • 2,530
  • 2
  • 28
  • 27
  • 5
    Note that while the first link looks awesome, it's in alpha and not likely to ever come out of it (last updated in 2008). – John Salvatier Dec 17 '10 at 17:30
2

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).

Plus of course they could form a closure, another whole layer of structure.

Richard
  • 106,783
  • 21
  • 203
  • 265
1

To answer your question, here's the Visual Studio Program Manager's official explanation of why you can't do this. In short, because "it's really, really hard" to implement in VS. But the feature is currently in progress (as updated on Aug 2014).

Allow the evaluation of lambda expressions while debugging

Add your vote while you're there!

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
Francisco d'Anconia
  • 2,436
  • 1
  • 19
  • 25
1

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. 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:

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}

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.

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

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
user8128167
  • 6,929
  • 6
  • 66
  • 79
1

In VS 2015 you can do so now,this is one of the new feature they added.

loneshark99
  • 706
  • 5
  • 16