这个环境上下文怎么会变成空呢?
谁能帮我解释一下 TimeProvider.Current
如何在下面的类中变为 null ?
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 的单元测试也会在其 Fixture Teardown 中调用 ResetToDefault()。
- 不涉及多线程代码。
- 有时,其中一个单元测试会失败,因为
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,因此该问题似乎与线程无关。
Can anyone help me explain how TimeProvider.Current
can become null in the following class?
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;
}
}
Observations
- All unit tests that directly reference TimeProvider also invokes ResetToDefault() in their Fixture Teardown.
- There is no multithreaded code involved.
- Once in a while, one of the unit tests fail because
TimeProvider.Current
is null (NullReferenceException is thrown). - This only happens when I run the entire suite, but not when I just run a single unit test, suggesting to me that there is some subtle test interdependence going on.
- It happens approximately once every five or six test runs.
- When a failure occurs, it seems to be occuring in the first executed tests that involves
TimeProvider.Current
. - More than one test can fail, but only one fails in a given test run.
FWIW, here's the DefaultTimeProvider class as well:
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; }
}
}
I suspect that there's some subtle interplay going on with static initialization where the runtime is actually allowed to access TimeProvider.Current
before all static initialization has finished, but I can't quite put my finger on it.
Any help is appreciated.
FWIW I just threw
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
in the getter, and it consistently reports the same ID for all test cases in a test run, so the issue seems not related to threading.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
仅基于此代码,
Current
可能为null
,因为它被设置为null
。这显然对你没有帮助。您能提供测试代码吗?如果存在测试相互依赖性,那么读者提供反馈将会很有帮助。
同时,Jon Skeet 关于单例的文章可能会有所帮助,因为
DefaultTimeProvider
实际上充当单例:http://csharpindepth.com/Articles/General/Singleton.aspxBased solely on this code,
Current
could benull
based on it being set tonull
. This obviously isn't helpful to you.Could you provide the code for the tests? If there's a test interdependence, it would be helpful for readers in order to provide any feedback.
In the mean time, possibly Jon Skeet's article on singletons might be helpful, since
DefaultTimeProvider
is effectively acting as a singleton: http://csharpindepth.com/Articles/General/Singleton.aspx感谢 Peter Ritchie 提供的链接,我可能对此有部分答案,尽管我无法完全解释发生了什么。 TimeProvider 和 DefaultTimeProvider 的静态初始化之间似乎存在某种竞争。它可能与 beforefieldinit 有关。
改变实施似乎已经解决了这个问题。如果不是,它肯定会使竞争条件变得更加罕见,以至于我还没有看到它。
我将 TimeProvider 的初始化更改为:
而 DefaultTimeProvider 则简单地更改为:
现在只有一个静态初始化程序在运行 (TimeProvider),并且由于它是一个显式静态构造函数,因此该类未标记为 beforefieldinit。
这似乎已经达到了目的......
I may have a partial answer to this, thanks to the links provided by Peter Ritchie, although I can't fully explain what's going on. It would seem that there was some kind of race going on between static initilization of TimeProvider and DefaultTimeProvider. It may have to do with beforefieldinit.
Changing the implementation seems to have resolved the issue. If not, it has certainly made the race condition much rarer, to a point where I have yet to see it.
I changed initialization of TimeProvider to this:
And DefaultTimeProvider simply to this:
There is now only one static initializer in play (TimeProvider) and since it's an explicit static constructor the class is not marked beforefieldinit.
This seems to have done the trick...