环境: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秒减少到900ms,仍然会有短暂的停顿,但是我认为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
而当我在野田时间应用它时:
[DebuggerDisplay("{ToString()}")]
public struct Instant
一个简单的测试应用程序会向我显示正确的结果:
instant "1970-01-01T00:00:00Z"
因此,大概是Noda Time中的问题是某种情况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
会导致它尝试更努力。