Навколишнє середовище: Visual Studio 2015 RTM. (Я не пробував старих версій.)
Нещодавно я налагоджував частину свого коду Noda Time , і я помітив, що коли я отримую локальну змінну типу NodaTime.Instant
(один із центральних struct
типів у Noda Time), вікна "Місцеві жителі" та "Дивитися" не здається, щоб викликати її 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()
дзвінка. І звичайно, це все-таки структура.
Я намагався повторювати, щоб побачити, чи це обмеження стека, але, здається, це не так.
Отже, як я можу розробити те, що зупиняє ВС від повної оцінки 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"
Тож, мабуть, проблема в Noda Time - це деяка умова, яка DebuggerDisplayAttribute
спрацьовує через те, що вона не діє через те, що вона не діє. (Це буде відповідати моєму очікуванню, що Instant.ToString
це досить швидко, щоб уникнути тайм-ауту.)
Це може бути досить хорошим рішенням - але я все одно хотів би знати, що відбувається, і чи можу я змінити код просто, щоб уникнути необхідності вводити атрибут у всі різні типи значень у Noda Time.
Curiouser і curiouser
Що б не плутало налагоджувач, лише його іноді плутає. Давайте створимо клас , який тримає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
спричинить це спробувати трохи складніше.