C#由Form的Show和ShowDialog以及Close的问题引发的关于Form机制的思考(一)

大约一年前吧,在某个技术交流群里出现了这么一个问题,大致上问的是Form在ShowDialog并调用了Close方法并没有及时释放,紧跟其后的语句仍然能读到这个Form的相关属性,也就是说程序并没有立即释放它,测试代码形如:

            Form2 frm2 = new Form2();
            if (frm2.ShowDialog() == DialogResult.OK)
                MessageBox.Show("Yes");
            else
                MessageBox.Show("No");
            frm2.Close();
            string strTest = frm2.strTest;

其中frm2里调用按钮的事件处理中调用了this.Close和this.DialogResult=DialogResult.OK。

当时工作仅一年,.Net本质论也还看的懵懵懂懂的,对Form的机制并不理解,当时根据对GC的理解,猜测是上下文的引用关系导致了GC没有及时释放它,今天,刚刚在群里又有人问了相似的问题,闲暇之余(好吧,我承认是工作时间偷懒了~)用Relector看了Form的相关方法,尝试理解了下它的机制,发现并不是当初想的那么回事儿,整个Form的机制也不像我们从表面上看的那么简单,它事实上采用了一种消息的机制来实现的,因为工作时间的关系,这里只重点看了涉及到相关问题的showdialog(IWin32Window)和show(IWin32Window)方法(无参的show方法其实也是调用了这个方法并传递了一个null参数),以及它重写了的Dispose方法,现就当前理解到的进行一个简单的分析,以留作备忘,待日后再做补足。

首先,先就问题而言,说一下跟问题相关的部分,逆向分析从上述代码来看就从Form的Close()着手吧~

public void Close()
{
    if (base.GetState(0x40000))
    {
        throw new                InvalidOperationException(SR.GetString("ClosingWhileCreatingHandle", new object[] { "Close" }));
    }
    if (base.IsHandleCreated)
    {
        this.closeReason = CloseReason.UserClosing;
        base.SendMessage(0x10, 0, 0);
    }
    else
    {
        base.Dispose();
    }
}
//这是base.Dispose方法,而不是Form重写后的
public void Dispose()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

这是Form的Close和base的Dispose方法,Form重写了那个接收一个bool类型参数的Dispose方法,此方法具体代码暂且不表,感兴趣的可以自己去看下,它简单的说就是当参数为true时置空释放一些句柄,如果有父窗体的话,就调用父窗体的RemoveOwnedForm(this)方法来移除父窗体对自身的调用,并将保存父窗体句柄的属性置空,如果有子窗体的话则循环调用子窗体的Dispose方法释放他们,再之后则调用base的Dispose(true),最后释放MainMenu相关的资源;而如果参数为false则直接调用base.Dispose(false),这之后是一层层的自身资源释放以及父类的Dispose方法的调用,我们可以简单的理解为Form一旦调用了上一层父类的Dispose则当前窗体就会得到释放(这里可以理解为告诉了GC和其他调用这些是要释放的资源,而真正的资源回收是由GC决定的,当然,你也可以主动的调用GC的Collect的方法使其回收这些它已知的要释放的资源),也就是说只有当IsHandleCreated的值为false,调用Form的Close方法才会使其立即得到“释放”,而我们在程序中像一开始所说的那样写一段简单的测试代码并加断点调式就会发现IsHandleCreated不论是show还是showdialog方法出示窗体,当此窗体调用Close时它的IsHandleCreated都为true,这也就是为什么Form在show以及showdialog之后调用了Close,而窗体并没有立即得到释放,它依然可以被访问的原因,而为什么IsHandleCreated为true以及这个属性是干毛的,先看一下msdn对Control.IsHandleCreated的解释(懒得截图了,直接复制文本):

如果已经为控件分配了句柄,则为 true;否则为 false

备注 

使用 IsHandleCreated 属性确定是否调用了 CreateHandle。

所以,一开始的问题的根源也就迎刃而解了,至于Form背后对这个属性的操作,以及由这个问题引发出的关于Form窗体的机制的问题的思考,下次有时间了再做整理,六点下班了,嘿嘿~

PS:欢迎大伙提出个人见解,如果有对这方面理解深刻的童鞋同样可以回复讲下自己的理解,我会在稍候的篇幅中对自己的理解进行阐述同时也对大家的理解进行归纳总结和相关测试~

 

 

posted @ 2012-10-16 18:05  MINI CodeMan CooperS  阅读(5332)  评论(0编辑  收藏  举报