Ambiente: Visual Studio 2015 RTM. (Non ho provato le versioni precedenti.)
Di recente, ho eseguito il debug di alcuni dei miei codici Noda Time e ho notato che quando ho una variabile locale di tipo NodaTime.Instant
(uno dei struct
tipi centrali in Noda Time), le finestre "Locals" e "Watch" non sembra chiamare la sua ToString()
sostituzione. Se chiamo ToString()
esplicitamente nella finestra di controllo, vedo la rappresentazione appropriata, ma altrimenti vedo solo:
variableName {NodaTime.Instant}
che non è molto utile.
Se cambio l'override per restituire una stringa costante, la stringa viene visualizzata nel debugger, quindi è chiaramente in grado di rilevare che è lì - semplicemente non vuole usarlo nel suo stato "normale".
Ho deciso di riprodurlo localmente in una piccola app demo, ed ecco cosa ho pensato. (Nota che in una prima versione di questo post, DemoStruct
era una classe e DemoClass
non esisteva affatto - colpa mia, ma spiega alcuni commenti che sembrano strani adesso ...)
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();
}
}
Nel debugger ora vedo:
demoClass {DemoClass}
demoStruct {Struct: Bar}
Tuttavia, se Thread.Sleep
riduco la chiamata da 1 secondo a 900 ms, c'è ancora una breve pausa, ma poi vedo Class: Foo
come valore. Non sembra importare per quanto tempo dura la Thread.Sleep
chiamata DemoStruct.ToString()
, viene sempre visualizzata correttamente - e il debugger visualizza il valore prima che la sospensione fosse completata. (È come se Thread.Sleep
fosse disabilitato.)
Ora Instant.ToString()
in Noda Time fa una buona dose di lavoro, ma sicuramente non ci vuole un secondo intero - quindi presumibilmente ci sono più condizioni che fanno sì che il debugger smetta di valutare una ToString()
chiamata. E ovviamente è comunque una struttura.
Ho provato a ricorrere per vedere se si tratta di un limite dello stack, ma sembra non essere così.
Quindi, come posso capire cosa impedisce a VS di valutare completamente Instant.ToString()
? Come indicato di seguito, DebuggerDisplayAttribute
sembra aiutare, ma senza sapere il perché , non sarò mai completamente fiducioso quando ne avrò bisogno e quando non lo farò.
Aggiornare
Se uso DebuggerDisplayAttribute
, le cose cambiano:
// For the sample code in the question...
[DebuggerDisplay("{ToString()}")]
public class DemoClass
mi da:
demoClass Evaluation timed out
Mentre quando lo applico in Noda Time:
[DebuggerDisplay("{ToString()}")]
public struct Instant
una semplice app di prova mi mostra il risultato giusto:
instant "1970-01-01T00:00:00Z"
Quindi, presumibilmente il problema in Noda Il tempo è una condizione che DebuggerDisplayAttribute
fa forza attraverso - anche se non forza attraverso timeout. (Questo sarebbe in linea con le mie aspettative che Instant.ToString
è abbastanza veloce abbastanza da evitare un timeout.)
Questa potrebbe essere una soluzione abbastanza buona, ma mi piacerebbe comunque sapere cosa sta succedendo e se posso cambiare il codice semplicemente per evitare di dover mettere l'attributo su tutti i vari tipi di valore in Noda Time.
Curioso e curioso
Tutto ciò che confonde il debugger lo confonde solo a volte. Creiamo una classe che detiene una Instant
e lo utilizza per il proprio ToString()
metodo:
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();
}
}
Ora finisco per vedere:
instant {NodaTime.Instant}
wrapper {1970-01-01T00:00:00Z}
Tuttavia, su suggerimento di Eren nei commenti, se cambio InstantWrapper
a essere una struttura, ottengo:
instant {NodaTime.Instant}
wrapper {InstantWrapper}
Quindi può valutare Instant.ToString()
- purché sia invocato da un altro ToString
metodo ... che è all'interno di una classe. La parte class / struct sembra essere importante in base al tipo di variabile visualizzata, non al codice che deve essere eseguito per ottenere il risultato.
Come altro esempio di ciò, se utilizziamo:
object boxed = NodaConstants.UnixEpoch;
... allora funziona benissimo, mostrando il giusto valore. Colorami confuso.
DebuggerDisplayAttribute
farebbe provare un po 'più difficile.