Может любой помогать мне объяснить как 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
является пустым (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);
в методе считывания, и это последовательно сообщает о том же идентификаторе для всех тестовых сценариев в тестовом прогоне, таким образом, проблема кажется не связанной с поточной обработкой.
Основываясь исключительно на этом коде, Current
может иметь значение null
, если установлено значение null
. Очевидно, это вам не поможет.
Не могли бы вы предоставить код для тестов? Если есть тестовая взаимозависимость, читателям было бы полезно дать обратную связь.
Между тем, возможно, статья Джона Скита о синглтонах может оказаться полезной, поскольку DefaultTimeProvider
эффективно действует как синглтон: http://csharpindepth.com/Articles/General/Singleton. aspx
Возможно, у меня есть частичный ответ на этот вопрос, благодаря ссылкам, предоставленным Питером Ричи, хотя я не могу полностью объяснить, что происходит. Похоже, что между статической инициализацией 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), и поскольку это явный статический конструктор, класс не помечается перед fieldinit.
Похоже, это помогло...