C#关闭窗体时出现ArgumentOutOfRangeException错误

4

我正在编写一个WinForms C#程序。

我试图关闭除了我的主窗体 FrmMain 之外的所有窗体。

我必须隐藏我的主窗体。

为了实现这个目标,我打开了两个窗体。一个是我的主窗体,另一个是使用 ShowDialog() 方法显示的另一个窗体。

当这段代码在我的电脑上执行时,它应该正确地关闭所有窗体。但出现了一个问题,当我没有设置断点并运行这个特定的代码片段时,我会收到一个 ArgumentOutOfRangeException 错误,因为变量 i 到达了 -1 的位置。但是当我设置断点并逐步执行每一行代码时,它就可以正常工作。

由于我已经加入了条件 i >= 0,所以for循环不应该一直继续下去,直到 i 变成 -1。这并不合理。

有人能解释一下为什么当我没有使用断点时,i 的索引会变成 -1,但是当我使用断点并逐行执行时,它会变成 0吗?

有什么方法可以解决这个问题?

提前感谢您的帮助。

            for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
            {
                if (Application.OpenForms[i] is FrmMain)
                {
                    Application.OpenForms[i]?.BeginInvoke((MethodInvoker)delegate
                    {
                        Application.OpenForms[i]?.Hide();
                    });
                }
                else
                {
                    Application.OpenForms[i]?.BeginInvoke((MethodInvoker)delegate
                    {
                        Application.OpenForms[i]?.Dispose();
                    });
                }
            }

编辑:

我避免抛出ArgumentOutOfRangeException的方法是在for循环内部添加了另一个变量。 这是我更改的代码。

            for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
            {
                int i1 = i;
                if (Application.OpenForms[i] is FrmMain)
                {
                    Application.OpenForms[i]?.BeginInvoke((MethodInvoker)delegate
                    {
                        Application.OpenForms[i1]?.Hide();
                    });
                }
                else
                {
                    Application.OpenForms[i]?.BeginInvoke((MethodInvoker)delegate
                    {
                        Application.OpenForms[i1]?.Dispose();
                    });
                }
            }

2
搜索词:“C#闭包” - Alexei Levenkov
你在哪里、何时以及多少次调用了这段代码? - Afshin Aghazadeh
@AfshinAghazadeh 它只在类中的新线程上被调用一次。 - Ben
1
有一件事需要注意,BeginInvoke 运行在一个单独的线程上,所以 i 可能已经改变了,当 Application.OpenForms[i]?.Dispose(); 被调用时,你可能得不到想要的行为。 - user783836
1个回答

1
基本上,for循环只有在条件为假时才终止,这意味着i必须达到-1才会停止。参见When does iteration variable in for loop increment,应该提供了一个很好的解释。
结合使用BeginInvoke,它基本上将执行移动到一个单独的线程(这意味着它的执行可能会延迟),并且i是一个捕获的变量,当调用Dispose()时,i完全可能为-1
当您设置断点时,您可能会减慢主线程的执行速度,使所有单独的线程按预期顺序运行,因此您不会看到问题。
您可以在此处了解有关捕获变量的信息:What are 'closures' in .NET?

1
我认为“完全可能”在这里不够强烈。我认为这是确定发生的事情。 - Enigmativity
谢谢。那帮了我大忙。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接