Closing or hiding forms causes a crossflow error

I am puzzled by this simple task, which I do again and again.

I have an array of child forms. The array is launched in another form constructor:

frmChildren = new ChildGUI[20]; 

When a user requests a child form, I do this:

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

While this is working . In the background, I can upload new content for these forms. When the download is complete, I fire the ChildChange event. Here it stops working. I just want to close / hide any forms, and then restore the new set -frmChildren = new ChildGUI [20]; - here is one of many tests:

  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]; 

I get a Cross Thread exception in .Close (). Notice that I already tried to make a call, but at the same time for some reason bypasses the value! = Null. I think this may have something to do with the garbage collector. Does anyone have an entrance?

+4
source share
1 answer

The problem is that your anonymous method captures i - therefore, by the time it is actually called in the user interface thread, you have another value i , which may be zero. Try the following:

 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]; 

See Eric Lippert's blog post on why introducing a new variable in a loop fixes the problem.

EDIT: if you want to use the foreach , it will look like this:

 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]; 

As an aside, you can use the fact that you just want to call the void method to make the code a little easier. Since this no longer uses the anonymous method, you can get rid of the "internal" variable:

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

Source: https://habr.com/ru/post/1301626/


All Articles