Visual Studio-Debugging-Tool "Quick Watch" und Lambda-Ausdrücke

96

Warum kann ich beim Debuggen im Fenster "Schnellüberwachung" keine Lambda-Ausdrücke verwenden?

UPD: siehe auch

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

lak-b
quelle
5
Dies wurde abgeschlossen und ist in der VS 2015-Vorschau verfügbar. visualstudio.uservoice.com/forums/121579-visual-studio/…
Francisco d'Anconia
Ich habe ein sehr einfaches Beispiel für MSDN für den Lambda-Ausdruck ausprobiert, aber es funktioniert nicht. Ich habe VS 2015 Enterprise Edition
Adeem
2
@ Franciscod'Anconia, um die Lambda-Unterstützung beim Debuggen zu aktivieren, muss "Verwalteten Kompatibilitätsmodus verwenden" deaktiviert sein ( stackoverflow.com/a/36559817/818321 ). Daher können Sie keine bedingten Haltepunkte verwenden: blogs.msdn .microsoft.com / devops / 2013/10/16 /… und stackoverflow.com/a/35983978/818321
Nik

Antworten:

64

Lambda-Ausdrücke sind wie anonyme Methoden tatsächlich sehr komplexe Tiere. Selbst wenn wir dies ausschließen Expression(.NET 3.5), bleibt dennoch eine Menge Komplexität, nicht zuletzt erfasste Variablen, die den Code, der sie verwendet, grundlegend neu strukturieren (was Sie als Variablen betrachten, werden zu Feldern in vom Compiler generierten Klassen). , mit ein bisschen Rauch und Spiegeln.

Daher bin ich nicht im geringsten überrascht, dass Sie sie nicht untätig verwenden können - es gibt eine Menge Compilerarbeit (und Typgenerierung hinter den Kulissen), die diese Magie unterstützt.

Marc Gravell
quelle
91

Nein, Sie können keine Lambda-Ausdrücke im Fenster watch / Locals / Instant verwenden. Wie Marc betont hat, ist dies unglaublich komplex. Ich wollte allerdings etwas weiter in das Thema eintauchen.

Was die meisten Leute bei der Ausführung einer anonymen Funktion im Debugger nicht berücksichtigen, ist, dass sie nicht in einem Vakuum auftritt. Das Definieren und Ausführen einer anonymen Funktion ändert die zugrunde liegende Struktur der Codebasis. Das Ändern des Codes im Allgemeinen und im Besonderen über das unmittelbare Fenster ist eine sehr schwierige Aufgabe.

Betrachten Sie den folgenden Code.

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

Dieser bestimmte Code erstellt einen einzelnen Abschluss, um den Wert v1 zu erfassen. Die Abschlusserfassung ist immer dann erforderlich, wenn eine anonyme Funktion eine Variable verwendet, die außerhalb ihres Gültigkeitsbereichs deklariert ist. In dieser Funktion existiert v1 in jeder Hinsicht nicht mehr. Die letzte Zeile sieht eigentlich eher wie folgt aus

var v3 = closure1.v1 + v2;

Wenn die Funktion Beispiel im Debugger ausgeführt wird, stoppt sie an der Break-Zeile. Stellen Sie sich nun vor, der Benutzer hätte Folgendes in das Überwachungsfenster eingegeben

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

Um dies ordnungsgemäß auszuführen, müsste der Debugger (oder besser der EE) einen Abschluss für die Variable v2 erstellen. Dies ist schwierig, aber nicht unmöglich.

Was dies für den EE wirklich schwierig macht, ist die letzte Zeile. Wie soll diese Zeile jetzt ausgeführt werden? In jeder Hinsicht hat die anonyme Funktion die Variable v2 gelöscht und durch Closure2.v2 ersetzt. Die letzte Codezeile muss also jetzt wirklich gelesen werden

var v3 = closure1.v1 + closure2.v2;

Um diesen Effekt tatsächlich im Code zu erzielen, muss der EE die letzte Codezeile ändern, bei der es sich tatsächlich um eine ENC-Aktion handelt. Dieses spezielle Beispiel ist zwar möglich, ein großer Teil der Szenarien jedoch nicht.

Was noch schlimmer ist, ist die Ausführung, dass der Lambda-Ausdruck keinen neuen Abschluss erzeugen sollte. Es sollten tatsächlich Daten an den ursprünglichen Abschluss angehängt werden. An dieser Stelle stoßen Sie direkt auf die Einschränkungen ENC.

Mein kleines Beispiel kratzt leider nur an der Oberfläche der Probleme, auf die wir stoßen. Ich sage immer wieder, ich schreibe einen vollständigen Blog-Beitrag zu diesem Thema und hoffe, dass ich dieses Wochenende Zeit habe.

JaredPar
quelle
41
Jammern, jammern, Mittelmäßigkeit akzeptieren, jammern, jammern. Der Debugger ist das Herz der IDE, und Sie haben es gebrochen! Lambdas im Überwachungsfenster müssen nichts erfassen. Wie jeder andere Überwachungscode sind sie nur am jeweiligen Stapelrahmen sinnvoll. (Oder Sie erfassen die Variable, wechseln zu einer anderen Funktion mit demselben Variablennamen ... und was?) Der Debugger soll den Compiler hacken. Bring es zum Laufen!
Aleksandr Dubinsky
2
Warum sie einfach keine erfassten Variablen auf Lambdas im Überwachungsfenster zulassen. Einfach und würde eine Reihe von Debug-Szenarien ermöglichen, in denen Lambdas nur in wirklich funktionalem Code verwendet werden.
Luiz Felipe
@LuizFelipe auch das ist noch ein massives Unterfangen . Der EE muss tatsächlich den gesamten Funktionskörper für den Rückruf generieren (bis hin zu IL). Der EE macht heute nichts dergleichen, stattdessen ist er ein Dolmetscher.
JaredPar
1
@ JaredPar können Sie Blog-Beitrag Marc sprechen über
Ehsan Sajjad
49

Sie können keine Lambda-Ausdrücke in den Fenstern "Sofort" oder "Beobachten" verwenden.

Sie können jedoch System.Linq.Dynamic-Ausdrücke verwenden , die die Form .Where ("Id = @ 0", 2) haben. Sie verfügen nicht über alle in Standard-Linq verfügbaren Methoden und nicht über alle Methoden Kraft der Lambda-Ausdrücke, aber dennoch ist es besser als nichts!

stusherwin
quelle
2
Nun ... während die anderen erklärten, dass dies nicht möglich war, bietet uns dieser zumindest eine mögliche Lösung. +1
Nullius
1
Zur Verdeutlichung "Importieren Sie System.Linq.Dynamic" und schreiben Sie dann im Debug-Fenster "Wo (etwas.AsQueryable," Eigenschaft> xyz ", nichts)"
grinsender
Das ist toll. Auch wenn Sie nicht das gesamte Spektrum der Linq Erweiterungsmethoden erhalten Sie, zum Beispiel gibt es keine .Any(string predicate), man kann : so etwas wie setzt .Where("Id>2").Any()im Watch - Fenster, oder PIN -Nummer zu. Es ist toll!
Beschützer ein
22

Die Zukunft ist gekommen!

Die Unterstützung für das Debuggen von Lambda-Ausdrücken wurde zu Visual Studio 2015 hinzugefügt ( Vorschau zum Zeitpunkt des Schreibens).

Der Ausdrucksauswerter musste neu geschrieben werden, daher fehlen viele Funktionen: Remote-Debugging von ASP.NET, Deklarieren von Variablen im Sofortfenster, Überprüfen dynamischer Variablen usw. Auch Lambda-Ausdrücke, die Aufrufe nativer Funktionen erfordern, werden derzeit nicht unterstützt.

Athari
quelle
2

Lambda-Ausdrücke werden vom Ausdrucksauswerter des Debuggers nicht unterstützt ... was nicht verwunderlich ist, da sie zur Kompilierungszeit eher zum Erstellen von Methoden (oder Ausdrucksbäumen) als von Ausdrücken verwendet werden (siehe Reflector mit auf .NET 2 umgeschalteter Anzeige) sieh sie dir an).

Außerdem könnten sie natürlich einen Verschluss bilden, eine weitere ganze Strukturschicht.

Richard
quelle
Nun, sie könnten Methoden erstellen; Sie könnten ExpressionBäume erstellen - das hängt vom Kontext ab.
Marc Gravell
1

In VS 2015 können Sie dies jetzt tun. Dies ist eine der neuen Funktionen, die sie hinzugefügt haben.

Loneshark99
quelle
1

Wenn Sie Visual Studio 2013 weiterhin verwenden müssen, können Sie tatsächlich eine Schleife oder einen Lambda-Ausdruck in das unmittelbare Fenster schreiben, indem Sie auch das Paketmanager-Konsolenfenster verwenden. In meinem Fall habe ich oben in der Funktion eine Liste hinzugefügt:

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

Wo meine GetAll()Funktion ist:

private DbSet<T> _dbSet;

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

    return list;
}

Hier wurde immer wieder der folgende Fehler angezeigt, daher wollte ich alle Elemente in den verschiedenen Repositorys ausdrucken:

InnerException {"Die DELETE-Anweisung stand in Konflikt mit der REFERENCE-Einschränkung" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId ". Der Konflikt trat in der Datenbank" CC_Portal_SchoolObjectModel ", Tabelle" dbo.Department "auf Anweisung wurde beendet. "} System.Exception {System.Data.SqlClient.SqlException}

Dann finde ich heraus, wie viele Datensätze sich im Abteilungsrepository befinden, indem ich dies im unmittelbaren Fenster ausführe:

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

Welches 243 zurückgegeben.

Wenn Sie also Folgendes in der Paketmanagerkonsole ausführen, werden alle Elemente ausgedruckt:

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

Den Autor der Idee finden Sie hier

user8128167
quelle
1

Um Ihre Frage zu beantworten, finden Sie hier die offizielle Erklärung des Visual Studio-Programmmanagers, warum Sie dies nicht tun können. Kurz gesagt, weil "es wirklich sehr, sehr schwer ist", in VS zu implementieren. Die Funktion ist jedoch derzeit in Bearbeitung (Stand: August 2014).

Ermöglichen Sie die Auswertung von Lambda-Ausdrücken während des Debuggens

Fügen Sie Ihre Stimme hinzu, während Sie dort sind!

Francisco d'Anconia
quelle