谁拥有控制权?(涉及IT技术)

5

假设我有这样一个组件:

class SomeForm : Form
{
    private Control example;

    public void Stuff()
    {
        this.example = new ComboBox();
        // ...
        this.Controls.Add(example);
    }

    public void OtherStuff()
    {
        this.Controls.Remove(example);
    }
}

谁负责调用示例控件上的Dispose方法?从this.Controls中删除它是否会导致其被处理?还是这会泄漏大量支持控件的窗口句柄? (供参考,我之所以问这个问题是因为我没看到Windows Forms Designer生成调用Form子控件的Dispose代码)
3个回答

5

Form.Dispose()会释放Controls集合中的控件,因此如果要从Controls中删除控件,则需要自行处理控件的释放。


3
当包含此控件的表单被处理时,您存储在Controls属性中的所有控件都将被处理。您不需要从集合中移除自定义控件。只需确保包含该控件的表单已被处理。
如果您从集合中删除控件,则该控件最终会超出范围并且无法进行垃圾回收。当GC运行时,它将调用finalizer/destructor,在Form类的情况下,它将简单地调用Dispose方法。这就是说,依赖于此是不好的做法。您应始终确保在完成与实现IDisposable接口的类一起使用后立即确定性(手动)调用Dispose方法。

控件不在“Controls”集合中,因为在处理父级之前,他将其从该集合中删除了。看起来你错过了整个问题的重点。 - Servy
抱歉,你是对的。我错过了他正在从集合中移除控件这一事实。如果您不处理控件,则它将超出范围,这意味着他将无法进行垃圾回收。当GC运行时,它将调用其Dispose方法。因此不会有内存泄漏,但最好的做法是在使用完实现IDisposable接口的类后立即调用Dispose方法,而不是等待GC来执行。 - Darin Dimitrov
@BillyONeal,GC 调用 finalizer/destructor。看一下 Form 类的 finalizer 做了什么。需要提示吗?它调用 Dispose 方法。 - Darin Dimitrov
1
@BillyONeal,但这不是内存泄漏!内存泄漏意味着您的程序使用越来越多的内存,直到最终出现OutOfMemoryException,而这在这里根本不是问题。如果机器上有足够的内存,则可能不会运行终结器。但是,一旦您的机器开始运行低内存,GC将运行并调用终结器,因此您的示例代码永远不会泄漏内存。但正如我已经解释过的那样,最好的做法是在您完成使用实现IDisposable接口的类后立即以确定性方式处理它们。 - Darin Dimitrov
@Darin:它泄漏了句柄。这与泄漏文件句柄或类似的情况没有区别。GC旨在收集其拥有的资源;HWND不是由GC拥有的,而是由窗口子系统拥有的。我相信每个进程的HWND数量有一个硬限制,大约为10,000个。 - Billy ONeal
显示剩余10条评论

0

有疑问就去源头查看:

Form.Dispose 看起来有点像这样:

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
       ... lots and lots of weird optimized checks ...
       base.Dispose(disposing);

好的...Form是一个ContainerControl,所以:

ContainerControl.Dispose:

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        this.activeControl = null;
    }
    base.Dispose(disposing);
    this.focusedControl = null;
    this.unvalidatedControl = null;
}

咕噜*...好的,ContainerControl 是一个 Control

Control.Dispose:

protected override void Dispose(bool disposing)
{
    ... a whole lot of resource reclaiming/funky code ...
     ControlCollection controls = (ControlCollection) 
            this.Properties.GetObject(PropControlsCollection);
     if (controls != null)
     {
         for (int i = 0; i < controls.Count; i++)
         {
              Control control = controls[i];
              control.parent = null;
              control.Dispose();
         }
         this.Properties.SetObject(PropControlsCollection, null);
      }
      base.Dispose(disposing);

是的,调用 Dispose 方法会释放包含在其中的控件。


然而,这并不是问题所问的;它询问的是当窗体被处理时,是否只有Controls集合中的控件被处理,这意味着如果他删除了控件(他确实这样做了),那么他现在是否需要负责处理它,或者是否有其他机制(例如从集合中删除控件时处理控件)可以防止他需要处理它。 - Servy
@Servy 我怎么会错过那个?更简单的答案是:不,从表单中弹出控件(从“Controls”中删除)基本上是将其与“Form”分离。 - JerKimball
1
值得注意的是,你可以创建一个继承自控件(Control)的类,重写Dispose方法并在其中添加一些日志记录功能,然后进行一系列实验以确定该方法是否被调用。虽然这并不是“证明”,但它是一个很好的起点。 - Servy
顺便提一下,另一个免费的反编译器(每当出现这样的问题时,我首先做的事情):http://ilspy.net/ - Tim M.
@TimMedora 哦,忘了这个 - 是的,我通常一直在运行 Reflector + dotPeek(它们以有趣的不同方式处理混淆代码) - JerKimball
显示剩余2条评论

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