为什么是SynchronizationContext.当前为空
本文关键字:SynchronizationContext 为什么 | 更新日期: 2023-09-27 18:05:47
错误: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);
}
}
}
您的代码在很大程度上取决于您的类的构造函数在何时何地运行。SynchronizationContext。
-
你的类对象创建得太快了,在你的代码创建Form类的实例或在Main()中调用Application.Run()之前。这时Current成员被设置为WindowsFormsSynchronizationContext的一个实例,这个类知道如何用消息循环封送调用。通过将对象实例化代码移动到主表单构造函数中来解决这个问题。
-
你的类对象是在除主UI线程之外的任何线程上创建的。只有Winforms应用程序中的UI线程才能封送调用。通过在类中添加一个构造函数来诊断:
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
还将这一行添加到Program.cs中的Main()方法中。如果Output窗口中显示的值不同,则无法工作。通过将对象实例化代码移动到主表单构造函数中来解决这个问题,这样您就可以确保它在UI线程上运行。
我在测试框架中通过依赖注入创建WinForms时遇到了这个问题。最初我会捕获SynchronizationContext。当前在我的构造函数:
private readonly SynchronizationContext UISyncCtxt;
public MyWinFormViewModel ()
{
UISyncCtxt = SynchronizationContext.Current;
...
}
如果这个MyWinFormViewModel是在应用程序已经运行时创建的,那么它工作得很好,但是在测试工具中创建依赖关系图时不一定是这种情况。当由测试工具SynchronizationContext创建时。Current将为null,之后会有一个null引用异常。
我的解决办法是"懒惰地";按如下方式计算:
private SynchronizationContext _uisyncctxt;
private SynchronizationContext UISyncCtxt =>
_uisyncctxt ??= SynchronizationContext.Current;
当我真正需要上下文(更新表单上的控件)时,它已经确定存在(因为表单已经实例化)。
编辑:Peter Duniho提出了一个关于任意抓取同步上下文的有效观点。我最初的回答也使这个类对它的依赖不诚实,因为它依赖于这个上下文,但不通过构造函数或其他可注入方法请求它。由于这个类使用DI,我添加了一个名为IUISyncContext的依赖项,它具有以下签名:public interface IUISyncContext
{
SynchronizationContext UISyncContext { get; }
}
…视图模型的构造函数是:
private readonly SynchronizationContext UISyncCtxt;
public MyWinFormViewModel (IUISyncContext syncContext)
{
UISyncCtxt = syncContext.UISyncContext;
...
}
谢谢你的反馈,Peter。