任何人都可以帮我解释一下TimeProvider.Current
在下面的课程中如何变为空吗?
public abstract class TimeProvider
{
private static TimeProvider current =
DefaultTimeProvider.Instance;
public static TimeProvider Current
{
get { return TimeProvider.current; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
TimeProvider.current = value;
}
}
public abstract DateTime UtcNow { get; }
public static void ResetToDefault()
{
TimeProvider.current = DefaultTimeProvider.Instance;
}
}
观察
TimeProvider.Current
为null(抛出NullReferenceException)。TimeProvider.Current
的首次执行测试中发生。FWIW,这里也是DefaultTimeProvider类:
public class DefaultTimeProvider : TimeProvider
{
private readonly static DefaultTimeProvider instance =
new DefaultTimeProvider();
private DefaultTimeProvider() { }
public override DateTime UtcNow
{
get { return DateTime.UtcNow; }
}
public static DefaultTimeProvider Instance
{
get { return DefaultTimeProvider.instance; }
}
}
我怀疑静态初始化会发生一些微妙的相互作用,在所有静态初始化完成之前,实际上允许运行时访问TimeProvider.Current
,但我无法完全理解它。
感谢任何帮助。
FWIW我刚扔了
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
在getter中,并且它始终为测试运行中的所有测试用例报告相同的ID,因此该问题似乎与线程无关。
答案 0 :(得分:7)
仅基于此代码,Current
可能是null
,因为它被设置为null
。这显然对你没有帮助。
您能提供测试代码吗?如果存在测试相互依赖性,那么对于读者来说,提供任何反馈都会有所帮助。
与此同时,Jon Skeet关于单身人士的文章可能会有所帮助,因为DefaultTimeProvider
实际上是一个单身人士:http://csharpindepth.com/Articles/General/Singleton.aspx
答案 1 :(得分:6)
由于Peter Ritchie提供的链接,我可能会对此作出部分答复,但我无法完全解释正在发生的事情。似乎在TimeProvider的静态启动和DefaultTimeProvider之间存在某种竞争。它可能与beforefieldinit。
有关更改实施似乎解决了这个问题。如果没有,它肯定使竞争条件变得更加罕见,到了我还没有看到的地步。
我将TimeProvider的初始化更改为:
public abstract class TimeProvider
{
private static TimeProvider current;
static TimeProvider()
{
TimeProvider.current = new DefaultTimeProvider();
}
//...
}
而DefaultTimeProvider就是这样:
public class DefaultTimeProvider : TimeProvider
{
public override DateTime UtcNow
{
get { return DateTime.UtcNow; }
}
}
现在只有一个静态初始值设定器(TimeProvider),因为它是一个显式的静态构造函数,所以该类在beforefieldinit之前没有标记。
这似乎已经成功了......