关于Form.Close跟Form.Dispose

我们在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;
      }
    }

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);
    }

其实在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; }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏進无尽的文章

编码篇-持久化NSUserDefaults

NSUserDefaults类,以字典形式保存数据,IOS会自动把字典中的键值对转换成对应的XML文件(也就是plist文件),这个文件会被保存到APP的沙盒目...

1141
来自专栏陈满iOS

iOS网络请求之上传图片:从示例到源码解析 -- 以上传Face++SDK回调的图片为例(HYNetworking,AFNetworking,XMNetworking)

本文一开始上传图片以调用HYNetworking的API为例,这个网络框架是以AFNetworking为基础进行的封装。HYNetworking内部实现上传图片...

1582
来自专栏美团技术团队

ReactiveCocoa中潜在的内存泄漏及解决方案

ReactiveCocoa是GitHub开源的一个函数响应式编程框架,目前在美团App中大量使用。用过它的人都知道很好用,也确实为我们的生活带来了很多便利,特别...

4778
来自专栏哈雷彗星撞地球

iOS 中如何判断当前是2G/3G/4G/5G/WiFi

5G 什么的,还得等苹果API更新啊,不过将来还是这个处理过程就是了。 关于判断当前的网络环境是2G/3G/4G,这个问题以前经常看到,最近在一工程里看到了如...

1222
来自专栏DannyHoo的专栏

第三方库AF之Post

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

791
来自专栏向治洪

RCTEventEmitter使用

在0.27版本之前,RN的Native端向js端发射消息主要通过sendDeviceEventWithName的方式,相关代码如下。 @synthesize b...

2637
来自专栏iOS122-移动混合开发研究院

RDVTabBarController--可自由定制的iOS底部导航控件

RDVTabBarController:一个十分完善的tabBarController,可以自定义角标个数,爽的停不下来。 RDVTabBarControlle...

21110
来自专栏一“技”之长

iOS获取通讯录联系人信息 原

随着apple对用户隐私的越来越重视,IOS系统的权限设置也更加严格,在获取系统通讯录之前,我们必须获得用户的授权。权限申请代码示例如下:

1683
来自专栏流柯技术学院

使用loadrunner进行压力测试之----post请求

2. 如果要发送的请求的数据值需要变化,那么需要将请求中的值参数化,,如果是根据上一条请求的返回值来确定请求中的数据值,那么需要对上一条请求的返回值进行解析

861
来自专栏陈满iOS

[iOS学习笔记]·FMDB:第三方数据库处理框架·用法示例篇(附源码Demo)

FMDatabase的实例对象_db在执行SQL语句的时候采取类似下面的代码。其中,有两种风格,一种在executeUpdate方法后面直接写上字符串的字面量语...

912

扫码关注云+社区