C# 清理集装箱的最安全方法是什么;s Controls属性,并确保正确处理控件?

C# 清理集装箱的最安全方法是什么;s Controls属性,并确保正确处理控件?,c#,winforms,gdi+,C#,Winforms,Gdi+,在WinForms应用程序中,我有许多实例,其中我向容器添加控件以响应用户操作(panel.Controls.add(new CustomControl(…)),然后清除面板(panel.Controls.clear())并重用它 在生产中,该应用程序偶尔会抛出与GDI错误或未能加载图像列表相关的异常。这通常发生在资源有限的机器上,以及一天中大量使用应用程序的用户。很明显,我有一个GDI句柄泄漏,我应该处理从容器中清除的控件,但是我能找到的任何解释都不清楚应该在何时何地处理控件 是否应在清理容器

在WinForms应用程序中,我有许多实例,其中我向容器添加控件以响应用户操作(
panel.Controls.add(new CustomControl(…)
),然后清除面板(
panel.Controls.clear()
)并重用它

在生产中,该应用程序偶尔会抛出与GDI错误或未能加载
图像列表相关的异常。这通常发生在资源有限的机器上,以及一天中大量使用应用程序的用户。很明显,我有一个GDI句柄泄漏,我应该处理从容器中清除的控件,但是我能找到的任何解释都不清楚应该在何时何地处理控件

是否应在清理容器后立即处置子控件?比如:

var controls = new List<Control>(_panel.Controls.Cast<Control>());
_panel.Controls.Clear();
foreach (var c in controls) c.Dispose();

选项2引入了另一个需要清理的列表,这些项目需要更多的内存。我更喜欢选项1,在你提到的代码周围有一个try-catch。

在(某种程度上有效地)纠正了我的应用程序没有处理清除控件的任何情况后,我可以提出以下几点:

  • 有时我预先构建了一个控件列表,例如存储在
    ListViewItem
    s或
    treevieItem
    s集合的
    Tag
    属性中。不应在清除时释放它们,但应迭代整个列表,并在父级的
    Dispose()
    方法中调用
    ((Control)item.Tag).Dispose()
  • 如果控件不再被使用(当我动态创建它时可能会发生这种情况),那么应该在从容器中清除它时将其释放
  • 当清除和添加对苍蝇的控制时,需要考虑控件的生命周期,以确定是否立即处理它们,推迟它直到父对象被处理,或者不担心它。
  • 我曾经有过这样一种情况:我删除了一个控件以显示一条“正在加载…”消息,然后在线程完成时将该控件重新放入。我在删除控件时添加了一个释放控件的调用,这在尝试再次添加控件时导致错误。由于线程问题,调试起来并不简单。关键是生命周期可以依赖于UI线程以外的线程。最恰当的例子是表单显示后20秒左右,因此至少控件仍然存在。对于弱事件来说,在线程仍然希望引用控件的情况下管理控件可能会被销毁的情况可能是一种情况

我还没有找到任何关于管理控制生命周期和处置的最佳实践或建议。我猜规则是,如果控件没有结束嵌套在已释放控件上的生命,则必须手动释放它,无论何时不再使用它,或最晚在父控件的
Dispose()
方法中释放它。

因此,不必在容器的
Dispose()
方法中释放控件?我一清理完集装箱就可以做吗?
List<Control> _controlsToDispose = new List<Control>();
void ClearControls()
{
    _controlsToDispose.AddRange(_panel.Controls.Cast<Control>());
    _panel.Controls.Clear();
}
void Dispose()
{
    ...
    foreach (var c in _controlsToDispose) c.Dispose();
}