Почему SynchronizationContext.Current равен нулю?
Ошибка: Object reference not set to an instance of an object.
Алгоритм ниже работает. Я попробовал это, затем я удалил Winform
проецировать в другой каталог и SynchronizationContext.Current
является null
, Зачем?
SynchronizationContext uiCtx = SynchronizationContext.Current;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int[] makeSelfMoves = new int[4];
lock (replay)
{
// count should be more than 2
foreach (KeyValuePair<int, int[]> item in replay)
{
makeSelfMoves = replay[item.Key];
codeFile.ExecuteAll(makeSelfMoves[0],
makeSelfMoves[1], makeSelfMoves[2], makeSelfMoves[3]);
// i get the error here. uictx is null
uiCtx.Post(o =>
{
PrintPieces(codeFile.PieceState());
}, null);
System.Threading.Thread.Sleep(1000);
}
}
}
2 ответа
Ваш код критически зависит от того, когда и где работает конструктор вашего класса. SynchronizationContext.Current будет нулевым, когда:
Ваш объект класса создается слишком рано, прежде чем ваш код создаст экземпляр класса Form или вызовет Application.Run() в Main(). Это когда текущий элемент установлен на экземпляр WindowsFormsSynchronizationContext, класса, который знает, как маршализовать вызовы с помощью цикла сообщений. Исправьте это, переместив код экземпляра вашего объекта в основной конструктор формы.
Ваш объект класса создается в любом потоке, кроме основного потока пользовательского интерфейса. Только поток пользовательского интерфейса в приложении Winforms может маршалировать вызовы. Диагностируйте это, добавив конструктор в ваш класс с помощью этого оператора:
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
Также добавьте эту строку в метод Main () в Program.cs. Это не будет работать, если отображаемое значение в окне вывода отличается. Исправьте это, переместив код экземпляра объекта в конструктор основной формы, чтобы вы могли быть уверены, что он выполняется в потоке пользовательского интерфейса.
Я столкнулся с этой проблемой при создании WinForms с помощью внедрения зависимостей в моей тестовой среде. Первоначально я бы зафиксировал SynchronizationContext.Current в своем конструкторе:
private readonly SynchronizationContext UISyncCtxt;
public MyWinFormViewModel ()
{
UISyncCtxt = SynchronizationContext.Current;
...
}
Это сработало нормально, если эта MyWinFormViewModel была создана, когда приложение уже работало, но это не обязательно ситуация при создании графа зависимостей в тестовой системе. При создании тестовой оснасткой SynchronizationContext.Current будет иметь значение NULL, и позже возникнет исключение с нулевой ссылкой.
Мое решение было «лениво» оценить это следующим образом:
private SynchronizationContext _uisyncctxt;
private SynchronizationContext UISyncCtxt =>
_uisyncctxt ??= SynchronizationContext.Current;
К тому времени, когда мне действительно понадобится контекст (для обновления элементов управления в форме), он обязательно будет присутствовать (поскольку форма была создана).
EDIT: Питер Дунихо поднял вопрос о произвольном захвате контекста синхронизации. Мой первоначальный ответ также делает этот класс нечестным в отношении своих зависимостей, поскольку он полагается на этот контекст, но не запрашивает его через конструктор или другой вводимый метод. Поскольку этот класс использует DI, я добавил зависимость под названием IUISyncContext, которая имеет следующую сигнатуру:
public interface IUISyncContext
{
SynchronizationContext UISyncContext { get; }
}
... и конструктор для моей модели представления:
private readonly SynchronizationContext UISyncCtxt;
public MyWinFormViewModel (IUISyncContext syncContext)
{
UISyncCtxt = syncContext.UISyncContext;
...
}
Спасибо за отзыв, Питер.