環境:Visual Studio 2015 RTM。(私は古いバージョンを試していません。)
最近、Noda Timeのコードの一部をデバッグしていて、タイプのローカル変数NodaTime.Instant
(struct
Noda Timeの中心的なタイプの1つ)を取得すると、[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()
、野田時間ではかなりの量の作業が行われますが、1秒もかかりません。おそらく、デバッガーがToString()
呼び出しの評価をやめる原因となる条件がさらにあると考えられます。そしてもちろん、とにかくそれは構造体です。
スタック制限であるかどうかを確認するために再帰的に試しましたが、そうではないようです。
だから、どうすればVSが完全に評価するのを止めているのかを知ることができInstant.ToString()
ますか?以下に示すように、DebuggerDisplayAttribute
助けになるように見えますが、理由がわからないので、必要なときと必要としないときを完全に確信することは決してありません。
更新
を使用するとDebuggerDisplayAttribute
、状況が変わります。
// For the sample code in the question...
[DebuggerDisplay("{ToString()}")]
public class DemoClass
私に与える:
demoClass Evaluation timed out
野田タイムで適用すると:
[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}
ただし、コメントでのErenの提案でInstantWrapper
、構造体に変更すると、次のようになります。
instant {NodaTime.Instant}
wrapper {InstantWrapper}
したがって、それは評価することができますInstant.ToString()
- それがToString
クラス内にある別のメソッドによって呼び出される限り... クラス/構造体の部分は、結果を取得するために実行する必要のあるコードではなく、表示されている変数のタイプに基づいて重要であると思われます。
これの別の例として、私たちが使用する場合:
object boxed = NodaConstants.UnixEpoch;
...その後、適切に機能し、適切な値を表示します。私を混乱させてください。
DebuggerDisplayAttribute
もう少し頑張ろうとするでしょう。