Как этот Окружающий Контекст может стать пустым?

Может любой помогать мне объяснить как 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 также вызывает ResetToDefault () в их Разрушении Приспособления.
  • Нет никакого многопоточного включенного кода.
  • Время от времени, один из модульных тестов перестали работать потому что 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);

в методе считывания, и это последовательно сообщает о том же идентификаторе для всех тестовых сценариев в тестовом прогоне, таким образом, проблема кажется не связанной с поточной обработкой.

12
задан Mark Seemann 15 May 2010 в 19:54
поделиться

2 ответа

Основываясь исключительно на этом коде, Current может иметь значение null , если установлено значение null . Очевидно, это вам не поможет.

Не могли бы вы предоставить код для тестов? Если есть тестовая взаимозависимость, читателям было бы полезно дать обратную связь.

Между тем, возможно, статья Джона Скита о синглтонах может оказаться полезной, поскольку DefaultTimeProvider эффективно действует как синглтон: http://csharpindepth.com/Articles/General/Singleton. aspx

7
ответ дан 2 December 2019 в 21:22
поделиться

Возможно, у меня есть частичный ответ на этот вопрос, благодаря ссылкам, предоставленным Питером Ричи, хотя я не могу полностью объяснить, что происходит. Похоже, что между статической инициализацией 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.

Похоже, это помогло...

6
ответ дан 2 December 2019 в 21:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: