我们在Winform开发的时候,使用From.Show来显示窗口,使用Form.Close来关闭窗口。熟悉Winform开发的想必对这些非常熟悉。但是Form类型实现了IDisposable接口,那我们是否需要每次关闭窗口后都去调用Dispose呢?对于这个问题我们可以查看一下Form的源码。

Form.Close
 public void Close()
{
if (this.GetState(262144))
throw new InvalidOperationException(SR.GetString("ClosingWhileCreatingHandle", new object[1]
{
(object) "Close"
}));
else if (this.IsHandleCreated)
{
this.closeReason = CloseReason.UserClosing;
this.SendMessage(16, 0, 0);
}
else
base.Dispose();
}
很明显这个方法有3个分支。第一个分支是关闭出现异常的情况,第二个分支是句柄已经创建的时候执行,很明显第三个分支的时候直接调用了基类的Dispose方法。大部分时候窗口调用Close时句柄肯定是被创建了,那就会进入第二个分支。很明显第二个分支没有调用Dispose,那是不是真的呢?继续跟进去。
 
SendMessage  
internal IntPtr SendMessage(int msg, int wparam, int lparam)
{
return UnsafeNativeMethods.SendMessage(new HandleRef((object) this, this.Handle), msg, wparam, lparam);
}
调了一个静态方法继续进去
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
public static IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);

明白了吧。SendMessage其实是调用了user32.dll的API,向指定的窗口发送消息。这里就是向自己发送一个消息,注意msg=16哦。既然有发送消息的,那肯定得有拦截消息的方法啊。(这里多扯一句,.NET Winform使用了事件驱动机制,事件机制其实也是封装了消息机制。)

WndProc
    protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 529:
this.WmEnterMenuLoop(ref m);
break;
case 530:
this.WmExitMenuLoop(ref m);
break;
case 533:
base.WndProc(ref m);
if (!this.CaptureInternal || Control.MouseButtons != MouseButtons.None)
break;
this.CaptureInternal = false;
break;
case 546:
this.WmMdiActivate(ref m);
break;
case 561:
this.WmEnterSizeMove(ref m);
this.DefWndProc(ref m);
break;
case 562:
this.WmExitSizeMove(ref m);
this.DefWndProc(ref m);
break;
case 288:
this.WmMenuChar(ref m);
break;
case 293:
this.WmUnInitMenuPopup(ref m);
break;
case 274:
this.WmSysCommand(ref m);
break;
case 279:
this.WmInitMenuPopup(ref m);
break;
case 167:
case 171:
case 161:
case 164:
this.WmNcButtonDown(ref m);
break;
case 71:
this.WmWindowPosChanged(ref m);
break;
case 130:
this.WmNCDestroy(ref m);
break;
case 132:
this.WmNCHitTest(ref m);
break;
case 134:
if (this.IsRestrictedWindow)
this.BeginInvoke((Delegate) new MethodInvoker(this.RestrictedProcessNcActivate));
base.WndProc(ref m);
break;
case 16:
if (this.CloseReason == CloseReason.None)
this.CloseReason = CloseReason.TaskManagerClosing;
this.WmClose(ref m);

break;
case 17:
case 22:
this.CloseReason = CloseReason.WindowsShutDown;
this.WmClose(ref m);
break;
case 20:
this.WmEraseBkgnd(ref m);
break;
case 24:
this.WmShowWindow(ref m);
break;
case 36:
this.WmGetMinMaxInfo(ref m);
break;
case 1:
this.WmCreate(ref m);
break;
case 5:
this.WmSize(ref m);
break;
case 6:
this.WmActivate(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

WndProc就是用来拦截窗口消息的。看一下代码,Form重写了这个方法,一个很简单的switch。Case 16调用了 WmClose方法,继续跟进去。

WmClose
private void WmClose(ref Message m)
{
FormClosingEventArgs e1 = new FormClosingEventArgs(this.CloseReason, false);
if (m.Msg != 22)
{
if (this.Modal)
{
if (this.dialogResult == DialogResult.None)
this.dialogResult = DialogResult.Cancel;
this.CalledClosing = false;
e1.Cancel = !this.CheckCloseDialog(true);
}
else
{
e1.Cancel = !this.Validate(true);
if (this.IsMdiContainer)
{
FormClosingEventArgs e2 = new FormClosingEventArgs(CloseReason.MdiFormClosing, e1.Cancel);
foreach (Form form in this.MdiChildren)
{
if (form.IsHandleCreated)
{
form.OnClosing((CancelEventArgs) e2);
form.OnFormClosing(e2);
if (e2.Cancel)
{
e1.Cancel = true;
break;
}
}
}
}
Form[] ownedForms = this.OwnedForms;
for (int index = this.Properties.GetInteger(Form.PropOwnedFormsCount) - 1; index >= 0; --index)
{
FormClosingEventArgs e2 = new FormClosingEventArgs(CloseReason.FormOwnerClosing, e1.Cancel);
if (ownedForms[index] != null)
{
ownedForms[index].OnFormClosing(e2);
if (e2.Cancel)
{
e1.Cancel = true;
break;
}
}
}
this.OnClosing((CancelEventArgs) e1);
this.OnFormClosing(e1);
}
if (m.Msg == 17)
m.Result = (IntPtr) (e1.Cancel ? 0 : 1);
if (this.Modal)
return;

}
else
e1.Cancel = m.WParam == IntPtr.Zero;
if (m.Msg == 17 || e1.Cancel)
return;
this.IsClosing = true;
if (this.IsMdiContainer)
{
FormClosedEventArgs e2 = new FormClosedEventArgs(CloseReason.MdiFormClosing);
foreach (Form form in this.MdiChildren)
{
if (form.IsHandleCreated)
{
form.OnClosed((EventArgs) e2);
form.OnFormClosed(e2);
}
}
}
Form[] ownedForms1 = this.OwnedForms;
for (int index = this.Properties.GetInteger(Form.PropOwnedFormsCount) - 1; index >= 0; --index)
{
FormClosedEventArgs e2 = new FormClosedEventArgs(CloseReason.FormOwnerClosing);
if (ownedForms1[index] != null)
{
ownedForms1[index].OnClosed((EventArgs) e2);
ownedForms1[index].OnFormClosed(e2);
}
}
FormClosedEventArgs e3 = new FormClosedEventArgs(this.CloseReason);
this.OnClosed((EventArgs) e3);
this.OnFormClosed(e3);
base.Dispose();
}

WmClose这个方法略有点复杂,主要是用来出发OnCloseing,OnClosed等事件。看看最后,它终于调用了base.Dispose()。看来Close方法确实会自动调用Dispose。是吗,不要高兴的太早。仔细看看前面的代码,if (this.Modal) return; 看到没,当窗口是模态的时候,方法直接return了。

总结

到这里就差不多了。所以当我们使用ShowDialog来显示窗体的时候,当你关闭的时候,最好手动Dispose一下。为什么是最好呢,因为其实在GC回收垃圾的时候还是会调用窗体的Dispose的,因为在Form的基类的终结器里面有调用Dispose(false);

   ~Component()
{
this.Dispose(false);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

其实在MSDN上微软就对这有说明,顺便吐槽一下中文MSDN的翻译,实在是太烂了。

有2种情况下需要手工调用Dispose:
1. 窗口是MDI的一部分且是不可见的

2.模态的时候
第二种情况就是现在说的,但是第一种情况我测试了下,没有复现出来,MDI里面的子窗口调用Close的时候跟正常一样,每次都会自动Dispose。试了很久还是一样的结果,求高人指定吧。

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

关于Form.Close跟Form.Dispose的更多相关文章

  1. Form.Close跟Form.Dispose

    关于Form.Close跟Form.Dispose   我们在Winform开发的时候,使用From.Show来显示窗口,使用Form.Close来关闭窗口.熟悉Winform开发的想必对这些非常熟悉 ...

  2. 使用Form Builder创建Form具体步骤

    使用Oracle Form Builder创建Form具体步骤 (Data Source为Table) 说明:当Block使用的Data Source为Table时,Form会自动Insert,Upd ...

  3. 知道Form.Show()和Form.ShowDialog()的区别吗

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:知道Form.Show()和Form.ShowDialog()的区别吗.

  4. form.Show()和form.ShowDialog()的区别、新建一个form和MessageBox.Show()的常见用法

    一:form.Show()和form.ShowDialog()的区别 a. 任何窗体(派生于基类Form的类),都可以以两种方式进行显示. //非模式窗体From qform=new Form();q ...

  5. $(#form :input)与$(#form input)的区别

    相信大家都很奇怪这两者的区别 我从两个方面简单介绍下 1. $("form :input") 返回form中的所有表单对象,包括textarea.select.button等    ...

  6. day75 form 组件(对form表单进行输入值校验的一种方式)

    我们的组件是什么呢 select distinct(id,title,price) from book ORM: model.py class Book(): title=model.CharFiel ...

  7. {Django基础十之Form和ModelForm组件}一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 ModelForm

    Django基础十之Form和ModelForm组件 本节目录 一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 Model ...

  8. http://www.vaikan.com/docs/jquery.form.plugin/jquery.form.plugin.html#getting-started

    http://www.vaikan.com/docs/jquery.form.plugin/jquery.form.plugin.html#getting-started Jquery.Form 异步 ...

  9. ExtJs6解决添加和修改Form共用一个form的隐藏域的id的取消传值

    问题重现:修改不会有问题,id会绑定之前的grid,有具体数字 添加有问题,因为id是空,传的是绑定值的话会显示“类名-1”,从int类型变成了string类型,后台会出错 这是EduQuestion ...

随机推荐

  1. 【Vegas原创】安装rhel6.2,不能进图形化界面的终极解决方法

    安装的时候,千万不要一路下一步,you should know,linux不是windows那么的傻瓜.   方法一: 在倒数最后一步,选择Desktop,而千万不要下一步,默认选择Basic Ser ...

  2. Codeforces Round #271 (Div. 2)题解【ABCDEF】

    Codeforces Round #271 (Div. 2) A - Keyboard 题意 给你一个字符串,问你这个字符串在键盘的位置往左边挪一位,或者往右边挪一位字符,这个字符串是什么样子 题解 ...

  3. 每日英语:A Different Color: China's Chameleonic Politics

    China has tried your form of government and found it wanting. That was the message delivered by Chin ...

  4. 使用 Python SimpleHTTPServer 快速共享文件

    近期,想着从一个服务器 向另一个服务器传输文件,但是对其知之甚少,就从别人那里知道一种方法,使用 Python SimpleHTTPServer 快速共享文件. 直接运行:python -m Simp ...

  5. 用户管理 之 Linux 用户(user)和用户组(group)管理概述

    一.理解Linux的单用户多任务,多用户多任务概念:Linux 是一个多用户.多任务的操作系统:我们应该了解单用户多任务和多用户多任务的概念: 1.Linux 的单用户多任务:单用户多任务:比如我们以 ...

  6. Navi.Soft30.产品.阅读导航

    Navi.Soft30.Core类库.开发手册 Navi.Soft30.框架.WinForm开发手册 Navi.Soft30.框架.WebMVC开发手册 Navi.Soft30.框架.Mobile.开 ...

  7. 获取发布版SHA1获取

  8. <img> to image_tag

    <img src="clean_wave.jpg?" alt="Clean_wave"><%= image_tag("clean_w ...

  9. 【总结】编写自己的JDBC框架

    一.数据库连接池: 在一般用JDBC 进行连接数据库进行CRUD操作时,每一次都会: 通过:java.sql.Connection conn = DriverManager.getConnection ...

  10. Ranorex 5 发布,支持SAP、Oracle Forms、MS Dynamics等

    Ranorex 5 发布,支持SAP.Oracle Forms.MS Dynamics等 http://blog.csdn.net/testing_is_believing/article/detai ...