Was macht der Visual Studio-debugger stop Auswertung eine ToString überschreiben?

Umgebung: Visual Studio 2015 RTM. (Ich habe nicht versucht, ältere Versionen.)

Kurzem habe ich das Debuggen einige meiner Noda Zeit code, und ich habe bemerkt, dass wenn ich habe eine lokale variable vom Typ NodaTime.Instant (eine der zentralen struct Arten in Noda-Zeit), die "Einheimischen" und "Watch" - Fenster nicht erscheinen, rufen seine ToString() überschreiben. Wenn ich den Anruf ToString() explizit in das watch-Fenster, sehe ich die entsprechende Darstellung, aber sonst kann ich nur sehen:

variableName       {NodaTime.Instant}

das ist nicht sehr nützlich.

Wenn ich die überschreiben, um wieder eine Konstante Zeichenfolge, die string ist angezeigt im debugger, so ist es eindeutig in der Lage zu Holen, dass es da - es funktioniert einfach nicht wollen, es zu benutzen in Ihrem "normalen" Zustand.

Entschied ich mich zum reproduzieren dieses lokal in einer kleinen demo-app, und hier ist, was ich mir ausgedacht habe. (Beachten Sie, dass in einer frühen version von diesem post, DemoStruct war Klasse und DemoClass gar nicht existierten - meine Schuld, aber es erklärt einige Kommentare, die seltsam Aussehen jetzt...)

using System;
using System.Diagnostics;
using System.Threading;

public struct DemoStruct
{
    public string Name { get; }

    public DemoStruct(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        Thread.Sleep(1000); //Vary this to see different results
        return $"Struct: {Name}";
    }
}

public class DemoClass
{
    public string Name { get; }

    public DemoClass(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        Thread.Sleep(1000); //Vary this to see different results
        return $"Class: {Name}";
    }
}

public class Program
{
    static void Main()
    {
        var demoClass = new DemoClass("Foo");
        var demoStruct = new DemoStruct("Bar");
        Debugger.Break();
    }
}

In den debugger, den ich nun zu sehen:

demoClass    {DemoClass}
demoStruct   {Struct: Bar}

Allerdings, wenn ich reduzieren die Thread.Sleep nennen, die von 1 Sekunde bis 900ms, gibt es noch eine kurze pause, aber dann sehe ich Class: Foo als Wert. Es scheint nicht zu Rolle, wie lange die Thread.Sleep Anruf in DemoStruct.ToString(), es ist immer richtig angezeigt und der debugger zeigt den Wert vor dem schlafen abgeschlossen haben. (Es ist, als wenn Thread.Sleep deaktiviert ist.)

Nun Instant.ToString() in Noda Zeit macht eine ganze Menge Arbeit, aber es ist sicherlich nicht eine ganze Sekunde - also vermutlich gibt es weitere Bedingungen, die verursachen, dass der debugger zu geben, die Bewertung einer ToString() nennen. Und natürlich ist es eine struct sowieso.

Habe ich versucht, recursing, um zu sehen, ob es ein stack-limit, aber das scheint nicht der Fall zu sein.

So, wie kann ich herausfinden, was hindert VS voll und ganz aus der Bewertung Instant.ToString()? Wie unten angegeben, DebuggerDisplayAttribute scheint zu helfen, aber ohne zu wissen warum, ich werde nie ganz Vertrauen, wenn ich es brauche, und wenn ich nicht.

Update

Wenn ich DebuggerDisplayAttribute, die Dinge ändern sich:

//For the sample code in the question...
[DebuggerDisplay("{ToString()}")]
public class DemoClass

gibt mir:

demoClass      Evaluation timed out

In der Erwägung, dass wenn ich es in Noda Time:

[DebuggerDisplay("{ToString()}")]
public struct Instant

einen einfachen test app zeigt mir das richtige Ergebnis:

instant    "1970-01-01T00:00:00Z"

Also vermutlich das problem in Noda Time ist eine Bedingung, dass DebuggerDisplayAttribute hat Kraft-durch - obwohl es nicht zwingen, durch timeouts,. (Dies wäre im Einklang mit meiner Erwartung, dass Instant.ToString ist einfach schnell genug, um einen timeout zu vermeiden.)

Diese kann eine ausreichend gute Lösung - aber ich würde noch gerne wissen, was Los ist, und ob ich das ändern kann der code einfach zu vermeiden, setzen Sie das Attribut auf alle die verschiedenen Werttypen in Noda Zeit.

Curiouser und curiouser

Was irreführend ist, ist der debugger nur verwirrt es manchmal. Zunächst erstellen wir eine Klasse, die hält eine Instant und verwendet es für seine eigenen ToString() Methode:

using NodaTime;
using System.Diagnostics;

public class InstantWrapper
{
    private readonly Instant instant;

    public InstantWrapper(Instant instant)
    {
        this.instant = instant;
    }

    public override string ToString() => instant.ToString();
}

public class Program
{
    static void Main()
    {
        var instant = NodaConstants.UnixEpoch;
        var wrapper = new InstantWrapper(instant);

        Debugger.Break();
    }
}

Nun habe ich am Ende zu sehen:

instant    {NodaTime.Instant}
wrapper    {1970-01-01T00:00:00Z}

Jedoch, auf Anregung von Eren in die Kommentare, wenn ich InstantWrapper eine struct, die ich bekomme:

instant    {NodaTime.Instant}
wrapper    {InstantWrapper}

Also es kann bewerten Instant.ToString() - so lange, wie das aufgerufen wird von einem anderen ToString Methode... die ist innerhalb einer Klasse. Die Klasse/struct Teil scheint wichtig zu sein, basierend auf dem Typ der Variablen angezeigt wird, nicht, welchen code muss
ausgeführt werden, um das Ergebnis zu erhalten.

Als ein weiteres Beispiel für diese, wenn, die wir verwenden:

object boxed = NodaConstants.UnixEpoch;

... dann funktioniert es einwandfrei, die Anzeige auf den richtigen Wert. Farbe, die mich verwirrt.

  • $"Struct: {Name}" ... was ist mit der $? Ist das ein neues feature oder doch eine andere Sache, die ich nie zuvor gesehen?!
  • gleiche Verhalten in VS 2013 (ich hatte zu entfernen die c#6 Sachen), mit einer zusätzlichen Nachricht: - Name-Funktion-Auswertung deaktiviert, da eine Vorherige Funktion Auswertung timed out. Sie müssen fortsetzen zu reaktivieren Funktion Auswertung. string
  • herzlich willkommen bei c# 6.0 @3-14159265358979323846264
  • Vielleicht ein DebuggerDisplayAttribute verursachen würde, es zu versuchen, ein wenig härter.
  • Aber was würde ich angeben, für wie formatieren? Der Punkt überschreiben von ToString ist, dass es schon das richtige format. Es ist nicht, wie ich auswählen möchten, die eine bestimmte Eigenschaft...
  • Wollte nur wissen ob verweist das Attribut auf ToString würde einen trick anwenden wartet ein wenig länger. (Oder machen Sie es vermeiden, das neue "fallen Sie zurück zu default ToString" Verhalten und scheitern anders???)
  • Versuchen [DebuggerDisplay("{ToString()}")] mit meinem Beispielcode ändert es zu "Evaluation timed out" :0
  • Gut, das ist vielleicht ein bisschen helfen :p
  • es ist der 5. Punkt neelbhatt40.wordpress.com/2015/07/13/... @3-14159265358979323846264 für den neuen c# 6.0
  • Das selbe Verhalten auf VS2010 Professional
  • Ooh - der Versuch, die in Noda Zeit funktioniert, für einige reason. Werde den Beitrag aktualisieren, wenn ich eine chance bekommen - vielen Dank! Würde noch gerne wissen, warum es benötigt wird, obwohl - merkwürdig...
  • Sie können ändern Sie den timeout in der registry ... stackoverflow.com/a/1212068/5040941. (Und danke @Neel)!
  • Erstellt habe ich "NormalEvalTimeout" und legen Sie es auf 2000 (hex, wurde 8192), und es noch mal wie beschrieben in der Frage. Der timeout-Wert nicht in der Registrierung vorhanden, so kann es nicht für 2015.
  • Mir ist auch aufgefallen, dass wenn ich eine timeout-parameter der Struktur, Konstrukt und erste mit 900 und dann mit 1000 (vor und nach dem Haltepunkt), die man mit 1000 zeigt die Art, sondern die eines mit 900 zeigt die ToString-Wert. Allerdings, wenn ich schalten Sie Sie um, beide zeigen den Typ. In anderen Worten, es scheint die einheimischen Fenster - / evaulation-engine merkt sich das Vorherige problem mit ToString und Beulen zurück zu dem Typ, wenn es denkt, das ist einfach zu langsam.
  • Recht. Das würde mich nicht überraschen - gut zu wissen, obwohl.
  • Ich bemerkte auch noch etwas anderes. Ich erstellte drei Variablen, die man vor dem Haltepunkt und zwei nach. Diese hatte timeouts von 800, 1000, 800 beziehungsweise. Wenn ich Schritt für Schritt durch das es zeigt, Struct:Bar, null, null, dann ist Struct:Bar, DemoStruct, null, dann DemoStruct, DemoStruct, DemoStruct. Es scheint, wenn es entdeckt, dass ToString nahm zu viel Zeit, irgendwann wird er bewertet den Inhalt und dann einfach die Standardwerte zurück zu dem Typ.
  • Beachten Sie, dass sehe ich exakt die gleichen Probleme, die Sie beschrieben haben, und die, die ich getestet habe in meinen vorherigen Kommentaren, auch wenn ich auf das gute alte String.Format. String-interpolation ist ein red herring (oder vielleicht bin ich einfach nicht verstehen, die bulletpoints am Ende Ihrer Frage)
  • Wenn du meinst, ob DebuggerDisplay verwendet, string interpolation, dann nicht. Diese syntax wurde erlaubt, eine ganze Weile, also sollte es auch problemlos auf älteren .NET-Versionen und Versionen von Visual Studio. Getestet habe ich diese in 2012 und es funktioniert tatsächlich identisch bis 2015 in Bezug auf, wie es zeigt nur ToString (wie ich beschrieben habe in meinen Kommentaren auch). ToString funktioniert mit der syntax, die Sie gezeigt haben.
  • Danke - haben die entfernt etwas rund um C# 6 als ein Ablenkungsmanöver.
  • Mit meinen 3 Strukturen und 800,1000,800 timeouts habe ich diese mit DebuggerDisplay-2012: Struct:Bar, Funktion Auswertung-Timeout, Funktion Auswertung deaktiviert, da eine Vorherige Funktion Auswertung timed out. Sie müssen fortsetzen zu reaktivieren Funktion Auswertung.
  • 2015 tatsächlich besser funktioniert, mit DebuggerDisplay. Wo 2012 gekennzeichnet haben, geben Sie als "nicht verwenden, DebuggerDisplay -, geben 'wurde deaktiviert "Fehlermeldung", 2015 haben neu zu bewerten DebuggerDisplay für meine 3. Instanz. 2015 gab mir Struktur:Bar, Bewertung timed out, Struct:Bar.
  • Die Frage die ich habe ist, warum die Instanz auslösen würde dies. Sicherlich die ToString-Methode, die nicht nehmen lange zu evaluieren. In anderen Worten, muss es andere Gründe als nur "1 Sekunde timeout" ausgelöst wird dieses Verhalten von der Rückkehr zu geben. Ich denke, das ist, warum Sie dies gepostet 🙂 Könnte es sein, dass die Summe aller einheimischen nicht mehr als 1 Sekunde? Testen...
  • Genau - es ist definitiv nicht nur ein timeout. (Hinweis, wie das Attribut macht die beiden Verhalten sich anders.) Ich dachte kurz, die es haben könnten, wurden aufgrund der Bewertung durch ein lock, aber dass doesn ' T scheinen es zu sein. (Die Summe aller einheimischen würden noch immer winzige, in dem Fall bin ich auf der Suche.)
  • Summe ist nicht der Fall. 3 Instanzen jeweils mit einem Zeitlimit von 800 bewertet, alles nur in Ordnung, wenn ich trat Sie an allen von Ihnen (Haltepunkt vor allen und, nachdem alle). Upping Sie die Zeitspanne der Mitte ein, um 1200 gab mir Struktur:Bar, DemoStruct, DemoStruct.
  • Ich habe versucht, die Zuweisung 86000 bytes tausend mal mit und ohne GC.Sammeln zwischen den einzelnen zu sehen, ob es verfolgt die Speicherauslastung oder Müll-Sammlungen, aber das änderte nichts.
  • Hmm. Ich bin ruhig verrückt auf diesen ein. Ich habe geändert, die Thread.Sleep zu 60000 und noch dem der debugger ausgeführt wird, es trifft den Zeilenumbruch viel schneller als eine minute und zeigt immer den benutzerdefinierten Wert, den ToString zurückkehren sollte.
  • Hmm, etwas entdeckt. Wenn eine Ausnahme geworfen wird ToString, die Anzeige in der einheimischen ist der Typ, der auch ohne schlafen. Könnte das sein? Beachten Sie, dass wenn Sie aktivieren Sie die code-Analyse, es wird explizit aufgerufen werden Ausnahmen geworfen wird, in der ToString-als DemoStruct.ToString () erzeugt eine Ausnahme vom Typ 'Ausnahme'. Ausnahmen sollten nicht erhöht werden, in diese Art von Methode. Wenn diese Ausnahme-Instanz erhoben werden könnten, ändern Sie diese Methode der Logik, damit es nicht mehr wirft eine exception. Dies ist CA1065.
  • Das Debuggen der Visual Studio-debugger als eine black-box ist sicherlich eine interessante übung, aber es ist wirklich eine produktive Nutzung Ihrer Zeit? Jemand mit dem source-code konnte schnell geben Sie eine definitive Antwort auf Ihre Frage, und vielleicht ist das problem auch lösen. Berücksichtigen aufgeben und mit einem der workarounds, die hier vorgeschlagen werden, oder (noch) hinzufügen einige logging-code, um zu erreichen das gleiche Ziel.
  • Keine Ausnahmen - und es ist gerade odder. Siehe mein update ("curiouser und curiouser").
  • Gut ich habe gefragt, es hier so, dass a) jemand, der entweder das gleiche gesehen, bevor oder weiß, die Innenseite von VS beantworten können; b) jemand mit den gleichen Problem in der Zukunft kann die Antwort schnell.
  • Mit meinem 3-Instanzen mit einem timeout von 800,1200,800 + 3 Wrapper, eine für jeden (mit der exakt gleichen tostring mit der exakt gleichen timeout) zeigt Struktur:Bar, 2x Typ, 3x Struct:Bar. Also ja, kurioser und kurioser.
  • Guter Punkt! Oder sogar c) finden Sie heraus, dass dies ein bekannter Fehler mit keine gute Antwort.
  • InstantWrapper ist eine Klasse und Instant ist ein struct. Vielleicht ist es wertet Klassen und Strukturen anders? Sollten Sie InstanceWrapper eine struct als gut.
  • Ooh, gut erkannt. Ja, das ändert die Dinge. Die Aktualisierung.
  • So viele Kommentare, die ich nicht bekommen kann durch Sie alle. Haben Sie versucht, um zu sehen, ob der Grenzwert für die Bewertung ist eigentlich Methode der Größe? Ähnlich wie die Grenze als Methode im Futter in Erwägung gezogen wird? Auch könnte der Unterschied zwischen einer struct-und class-zu tun haben mit dem Aufruf der Methode IL? Nicht sicher, was es sieht aus wie aus der hand, ich würde davon ausgehen, es ist das gleiche, aber vielleicht hat der compiler tut etwas extra, um den Wert Art wieder in ein object.
  • Ich wollte nicht Aussehen, aber der Körper des Instant.ToString Methode ist nur eine einzige Methode zu nennen, so dass ich bezweifle sehr, dass das relevant ist. (Guter Gedanke, aber.)
  • Tatsächlich, meine Beobachtungen wurden durchgeführt, in denen DemoStruct ist Klasse, da war es, dass in der ersten version dieser Frage. Ich habe irgendwann denken, dass es seltsam Sie mit dem Namen einer Klasse DemoStruct aber dachte, es war nur ein Artefakt der schwere Experimentieren, habe nicht bemerkt, dass Sie bearbeitet die Frage gestellt und verändert. Also meine Kommentare oben über timeouts und 3-Instanzen und-Wrapper und so, alles ist Klassen.
  • Hoppala - ja, ich habe bemerkt und behoben, aber nicht erhöhen... sind Sie leicht in der Lage zu überprüfen, ob die Beobachtungen tatsächlich noch halten?
  • Es sei denn, die kompilierte Methode bietet in-gefütterte Inhalt aus dem Aufruf der Methode? Vielleicht ist die IL ist größer als die C# können Sie sehen. Clutching at straws wirklich, kann nicht warten, um zu sehen, ein VS-dev diese eine Antwort 🙂
  • Es ändert. Mit Klassen, 3 Instanzen + 3 Wrapper (in dieser Reihenfolge), mit timeouts, 800, 1200, 800: tostring+2x Typ+3x tostring. Ändern der inneren Klasse struct: 4x tostring + 2x Typ. Ändern der wrapper-Struktur sowie: 6x tostring. Dies ist der code, dass nur Konstrukte alle 6 Instanzen (3 innere, dann 3 Wrapper), dann einen Haltepunkt erreicht. So scheint es die Regel ist, dass, wenn es eine Struktur, die wir gehen, um es zu bewerten, egal was, aber für Klassen gibt es einen timeout. Gut, außer für den wrapper verhält sich seltsam 🙂 ich bin auf der Abstimmung nach dem Zufallsprinzip. Dies ist new Random() in ein neues Gewand 🙂
  • Diese Regel klingt wie es sein könnte, basierend auf den vorhersehbaren Vererbungskette von struct Typen (dh keine) vs-Klassen, die könnte sein sehr kompliziert und kostspielig. Ich Frage mich, ob es sich um walking zu viel code, oder code, die über Bibliotheken oder so etwas.
  • Es könnte auch eine Lebenserwartung, die Strukturen sollen Licht-Gewicht Werte, in der Erwägung, dass Klassen sind in der Regel komplexer.
  • Auf eine ernstere note, die SO devs zu beheben müssen Sie die UI. Ihre badge-Zählungen sind zugeschnitten auf die Benutzer Kachel...
  • Das ist seltsam und passt nicht mit Instant ein struct. Ich habe gerade festgestellt, dass es noch seltsamer, obwohl - wenn ich eine 10-Sekunden-Schlaf in ToString, so scheint es, ignoriert zu werden, während Sie zu bewerten. Glaubst du, es lohnt sich mich ändern dies wieder eine Klasse, um die Frage mehr konsistent?
  • Aktualisiert haben die erste repro-app zu haben, sowohl eine struct und einer Klasse. Ärgerlicherweise heute morgen (als ich zuerst versuchte, repro dieser) hatte ich schon DemoStruct und DemoClass, aber DemoStruct war immer eine Klasse. Humbug. Ich saugen.
  • die Thread.Sleep scheinbar keine Wirkung haben, ist das, was ich war, reporting, Weg da oben ^^^^^^^.
  • Recht - und vermutlich ist Sie richtig hatte es wie ein struct? Es macht einen Unterschied, wenn es eine Klasse.
  • Ich habe das gleiche in VS2012 und nie eine Lösung gefunden.

InformationsquelleAutor Jon Skeet | 2015-07-22
Schreibe einen Kommentar