Среда: Visual Studio 2015 RTM. (Я не пробовал старые версии.)
Недавно я отлаживал часть своего кода Noda Time и заметил, что когда у меня есть локальная переменная типа NodaTime.Instant
(один из центральных struct
типов в Noda Time), окна «Locals» и «Watch» не кажется, чтобы вызвать его ToString()
переопределение. Если я вызываю ToString()
явно в окне просмотра, я вижу соответствующее представление, но в противном случае я просто вижу:
variableName {NodaTime.Instant}
что не очень полезно.
Если изменить переопределение вернуть постоянную строку, строка будет отображаться в отладчике, так что это явно в состоянии подобрать то , что она есть - она просто не хочет , чтобы использовать его в «нормальном» состоянии.
Я решил воспроизвести это локально в небольшом демонстрационном приложении, и вот что я придумал. (Обратите внимание, что в ранней версии этого поста, это DemoStruct
был класс, и его DemoClass
вообще не было - моя вина, но он объясняет некоторые комментарии, которые сейчас выглядят странно ...)
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();
}
}
В отладчике я теперь вижу:
demoClass {DemoClass}
demoStruct {Struct: Bar}
Тем не менее, если я Thread.Sleep
уменьшу количество вызовов с 1 секунды до 900 мсек, все равно будет короткая пауза, но тогда я вижу Class: Foo
значение. Кажется, не имеет значения, как долго выполняется Thread.Sleep
вызов DemoStruct.ToString()
, он всегда отображается правильно - и отладчик отображает значение до завершения спящего режима. (Это как будто Thread.Sleep
отключено.)
Сейчас Instant.ToString()
в Noda Time выполняется довольно много работы, но это, конечно, не занимает целую секунду - так что, вероятно, есть больше условий, которые заставляют отладчик отказаться от оценки ToString()
вызова. И, конечно, в любом случае это структура.
Я попытался повторить, чтобы увидеть, является ли это предел стека, но это не так.
Итак, как я могу понять, что мешает VS полностью оценить Instant.ToString()
? Как отмечено ниже, DebuggerDisplayAttribute
похоже, что это помогает, но, не зная почему , я никогда не буду полностью уверен, когда мне это нужно, а когда нет.
Обновить
Если я использую DebuggerDisplayAttribute
, все меняется:
// For the sample code in the question...
[DebuggerDisplay("{ToString()}")]
public class DemoClass
дает мне:
demoClass Evaluation timed out
Принимая во внимание, что, когда я применяю это в Noda Time:
[DebuggerDisplay("{ToString()}")]
public struct Instant
простое тестовое приложение показывает мне правильный результат:
instant "1970-01-01T00:00:00Z"
Так , предположительно, проблема в Ноде время некоторое условие , которое DebuggerDisplayAttribute
делает усилие через - даже если он не протолкнуть таймауты. (Это будет соответствовать моим ожиданиям, что Instant.ToString
достаточно быстро, чтобы избежать тайм-аута.)
Это может быть достаточно хорошим решением, но я все же хотел бы знать, что происходит, и могу ли я изменить код просто, чтобы избежать необходимости помещать атрибут во все различные типы значений в Noda Time.
Любопытнее и любопытнее
Что бы ни сбивало с толку, отладчик только иногда смущает это. Давайте создадим класс , который держитInstant
и использует его для своего собственного ToString()
метода:
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();
}
}
Теперь я вижу:
instant {NodaTime.Instant}
wrapper {1970-01-01T00:00:00Z}
Однако, по предложению Эрен в комментариях, если я изменяю InstantWrapper
на структуру, я получаю:
instant {NodaTime.Instant}
wrapper {InstantWrapper}
Таким образом, он может оценить Instant.ToString()
- при условии, что он вызывается другим ToString
методом ... который находится внутри класса. Часть класса / структуры кажется важной в зависимости от типа отображаемой переменной, а не от того, какой код необходимо выполнить, чтобы получить результат.
В качестве другого примера этого, если мы используем:
object boxed = NodaConstants.UnixEpoch;
... тогда он работает нормально, отображая правильное значение. Цвет меня смутил.
DebuggerDisplayAttribute
заставит его попробовать немного сложнее.