环境:Visual Studio 2015 RTM。 (我没有尝试过旧版本。)
最近,我一直在调试我的一些野田时间 http://nodatime.org代码,我注意到当我有一个类型的局部变量时NodaTime.Instant
(中央之一struct
野田时间类型),“本地”和“观看”窗口似乎没有调用其ToString()
覆盖。如果我打电话ToString()
明确地在监视窗口中,我看到了适当的表示,但除此之外我只是看到:
variableName {NodaTime.Instant}
这不是很有用。
如果我更改覆盖以返回常量字符串,则该字符串is显示在调试器中,因此它显然能够识别出它的存在 - 它只是不想在“正常”状态下使用它。
我决定在一个小演示应用程序中在本地重现此内容,这就是我的想法。 (请注意,在本文的早期版本中,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
被禁用。)
Now Instant.ToString()
在 Noda Time 中做了相当多的工作,但它肯定不需要一整秒 - 所以大概有更多的条件导致调试器放弃评估ToString()
称呼。当然,无论如何它都是一个结构体。
我尝试递归查看是否是堆栈限制,但情况似乎并非如此。
那么,我怎样才能找出是什么阻止了 VS 充分评估Instant.ToString()
?如下所述,DebuggerDisplayAttribute
看似有帮助,但不知道why,我永远不会对何时需要、何时不需要完全有信心。
Update
如果我使用DebuggerDisplayAttribute https://msdn.microsoft.com/en-us/library/system.diagnostics.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
does强制通过 - 即使它不强制通过超时。 (这符合我的预期Instant.ToString
很容易足够快以避免超时。)
This may是一个足够好的解决方案 - 但我仍然想知道发生了什么,以及我是否可以更改代码以避免必须将属性放在 Noda Time 中的所有各种值类型上。
越来越好奇
无论是什么令人困惑,调试器有时只会混淆它。让我们创建一个类holds an Instant
并将其用于自己的ToString()
method:
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}
So it can评价Instant.ToString()
- 只要被另一个调用ToString
方法...在类中。根据所显示变量的类型,类/结构部分似乎很重要,而不是代码需要的内容
来执行以获得结果。
作为另一个例子,如果我们使用:
object boxed = NodaConstants.UnixEpoch;
...然后它工作正常,显示正确的值。让我感到困惑。