Environnement: Visual Studio 2015 RTM. (Je n'ai pas essayé d'anciennes versions.)
Récemment, j'ai débogué une partie de mon code Noda Time , et j'ai remarqué que lorsque j'ai une variable locale de type NodaTime.Instant
(l'un des struct
types centraux de Noda Time), les fenêtres "Locals" et "Watch" ne semblent pas appeler son ToString()
remplacement. Si j'appelle ToString()
explicitement dans la fenêtre de surveillance, je vois la représentation appropriée, mais sinon je vois juste:
variableName {NodaTime.Instant}
ce qui n'est pas très utile.
Si je modifie le remplacement pour renvoyer une chaîne constante, la chaîne est affichée dans le débogueur, il est donc clairement capable de détecter qu'elle est là - il ne veut tout simplement pas l'utiliser dans son état "normal".
J'ai décidé de reproduire cela localement dans une petite application de démonstration, et voici ce que j'ai trouvé. (Notez que dans une première version de ce post, DemoStruct
c'était une classe et DemoClass
n'existait pas du tout - ma faute, mais cela explique certains commentaires qui semblent bizarres maintenant ...)
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();
}
}
Dans le débogueur, je vois maintenant:
demoClass {DemoClass}
demoStruct {Struct: Bar}
Cependant, si je Thread.Sleep
réduis l' appel de 1 seconde à 900 ms, il y a encore une courte pause, mais je vois Class: Foo
la valeur. Peu importe la durée de l' Thread.Sleep
appel DemoStruct.ToString()
, il s'affiche toujours correctement - et le débogueur affiche la valeur avant la fin du sommeil. (C'est comme si elle Thread.Sleep
était désactivée.)
Maintenant, Instant.ToString()
dans Noda, le temps fait beaucoup de travail, mais cela ne prend certainement pas une seconde entière - il y a donc probablement plus de conditions qui poussent le débogueur à abandonner l'évaluation d'un ToString()
appel. Et bien sûr, c'est une structure de toute façon.
J'ai essayé de répéter pour voir s'il s'agit d'une limite de pile, mais cela ne semble pas être le cas.
Alors, comment puis-je déterminer ce qui empêche VS d'évaluer complètement Instant.ToString()
? Comme indiqué ci-dessous, DebuggerDisplayAttribute
semble aider, mais sans savoir pourquoi , je ne serai jamais entièrement confiant quand j'en ai besoin et quand je n'en ai pas.
Mettre à jour
Si j'utilise DebuggerDisplayAttribute
, les choses changent:
// For the sample code in the question...
[DebuggerDisplay("{ToString()}")]
public class DemoClass
Donne moi:
demoClass Evaluation timed out
Alors que lorsque je l'applique dans Noda Time:
[DebuggerDisplay("{ToString()}")]
public struct Instant
une simple application de test me montre le bon résultat:
instant "1970-01-01T00:00:00Z"
On peut donc supposer le problème dans le temps Noda est une condition qui DebuggerDisplayAttribute
fait la force à travers - même si elle ne force pas par les délais d' attente. (Ce serait conforme à mon attente, qui Instant.ToString
est facilement assez rapide pour éviter un délai d'attente.)
Cela peut être une assez bonne solution - mais j'aimerais toujours savoir ce qui se passe et si je peux changer le code simplement pour éviter d'avoir à mettre l'attribut sur tous les différents types de valeurs dans Noda Time.
Curieux et curieux
Tout ce qui prête à confusion, le débogueur ne fait que le confondre parfois. Créons une classe qui contient un Instant
et l'utilise pour sa propre ToString()
méthode:
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();
}
}
Maintenant je finis par voir:
instant {NodaTime.Instant}
wrapper {1970-01-01T00:00:00Z}
Cependant, à la suggestion d'Eren dans les commentaires, si je change InstantWrapper
pour être une structure, j'obtiens:
instant {NodaTime.Instant}
wrapper {InstantWrapper}
Donc, il peut évaluer Instant.ToString()
- tant qu'il est invoqué par une autre ToString
méthode ... qui est dans une classe. La partie class / struct semble être importante en fonction du type de la variable affichée, et non du code à exécuter pour obtenir le résultat.
Comme autre exemple de cela, si nous utilisons:
object boxed = NodaConstants.UnixEpoch;
... alors cela fonctionne très bien, affichant la bonne valeur. Colorie-moi confus.
DebuggerDisplayAttribute
ferait essayer un peu plus.