2010-02-17 72 views
4

我很困惑这个简单的任务,我做了一遍又一遍。关闭或隐藏窗体会导致交叉线程错误

我有一组子表单。该阵列开始以另一种形式的构造函数:

frmChildren = new ChildGUI[20]; 

当用户请求看到一个孩子形式,我这样做:

if (frmChildren[nb] == null) 
{ 
    frmChildren[nb] = new ChildGUI(); 
    frmChildren[nb].MdiParent = this.MdiParent; 
} 
frmChildren[nb].Show(); 

到目前为止,这工作。在后台我可以下载这些表单的新内容。当下载完成时,我触发一个ChildChange事件。这是停止工作的地方。 我只是想关闭/隐藏任何形式的开放,然后重新生成一套新-frmChildren =新ChildGUI [20]; - 这里是许多试验之一:

 for (int i = 0; i < frmChildren.Length;i++) 
     { 
      if (frmChildren[i] != null) 
      { 
       //frmChildren[i].BeginInvoke(new EventHandler(delegate 
       //{ 
        frmChildren[i].Close(); 
       //})); 
      } 
     }    
     frmChildren= new ChildGUI[20]; 

我得到一个跨线程异常的。关()。注意我已经尝试过调用,但是这样做会绕过!= null出于某种原因。我认为这可能与垃圾收集器有关。任何人都有投入?

回答

9

的问题是,你的匿名方法捕获i - 这样的时候它实际上 UI线程调用,您已经有了一个i不同的值,它可以为null。试试这个:

for (int i = 0; i < frmChildren.Length; i++) 
{ 
    ChildGUI control = frmChildren[i]; 
    if (control != null) 
    { 
     control.BeginInvoke(new EventHandler(delegate 
     { 
      control.Close(); 
     })); 
    } 
}    
frmChildren = new ChildGUI[20]; 

为什么引入一个新的变量循环解决问题见Eric Lippert's blog post

编辑:如果你想使用一个foreach循环,它应该是这样的:

foreach (ChildGUI control in frmChildren) 
{ 
    // Create a "new" variable to be captured 
    ChildGUI copy = control; 
    if (copy != null) 
    { 
     copy.BeginInvoke(new EventHandler(delegate 
     { 
      copy.Close(); 
     })); 
    } 
}    
frmChildren = new ChildGUI[20]; 

正如顺便说一句,你可以使用的事实,你只是想调用一个无效的方法,使代码稍微简单一些。由于这不再使用匿名方法,因此可以取消“内部”变量:

foreach (ChildGUI control in frmChildren) 
{ 
    if (control != null) 
    { 
     control.BeginInvoke(new MethodInvoker(control.Close)); 
    } 
}    
frmChildren = new ChildGUI[20]; 
+1

我已经尝试了与上述相同的代码和foreach。一个foreach会如何不同? ODDLY您的解决方案的作品! +1 – Roast 2010-02-17 20:51:53

+1

感谢您的链接,解释为什么它的工作方式。如果你不关心异步关闭表单,我想你可以用Invoke替换BeginInvoke而不是引入另一个变量。由于Invoke是同步的,因此迭代变量'i'的值不会改变。 – 2010-02-17 21:02:07

+0

@Lily:使用foreach循环,您仍然需要在循环*中引入一个额外的变量*。这是重要的一点。 – 2010-02-17 21:46:30