2010-02-17 69 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