關於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的翻譯,實在是太爛了。

image

有2種情況下需要手工調用Dispose:
1. 窗口是MDI的一部分且是不可見的

2.模態的時候
第二種情況就是現在說的,但是第一種情況我測試了下,沒有復現出來,MDI里面的子窗口調用Close的時候跟正常一樣,每次都會自動Dispose。試了很久還是一樣的結果,求高人指定吧。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM