1. 程式人生 > >C# WinForm:無法訪問已釋放的物件

C# WinForm:無法訪問已釋放的物件

C#在父視窗中呼叫子視窗的過程: 1、 建立子視窗物件 2、 顯示子視窗物件   筆者的程式中,主窗體MainFrm通過選單呼叫子視窗ChildFrm。在窗體中定義了子視窗物件,然後在選單項點選事件中,加入瞭如下程式碼來建立和顯示子視窗:
Private childFrm myChildFrm = null; //定義子視窗物件
private void OpenChildFrmToolStripMenuItem_Click(object sender, EventArgs e)
{
        myChildFrm = new ChildFrm();//建立子視窗物件
        myChildFrm.Show();//顯示子視窗
        myChildFrm.Focus();//使子視窗獲得焦點
}

 

當點選選單中的OpenChild項時,建立了子視窗並顯示在最前面。此時如果關閉子視窗再點選選單開啟,不會有問題。但是如果子視窗沒有關閉的情況下,再次點選選單中的OpenChild項,則會再建立一個子視窗。兩個子視窗具有相同的內容,這不是我們所希望看到的。   為此,對選單項點選事件做如下改進:
private
void OpenChildFrmToolStripMenuItem_Click(object sender, EventArgs e) { if(myChildFrm != null) { myChildFrm.Show();//顯示子視窗 myChildFrm.Focus();//使子視窗獲得焦點 } else { myChildFrm = new ChildFrm();//建立子視窗物件 myChildFrm.Show();//顯示子視窗 myChildFrm.Focus();//使子視窗獲得焦點
} }
這樣修改的目的是:當子視窗物件存在時,直接顯示子視窗。當子視窗不存在時,建立子視窗,然後再顯示。   現在來檢驗效果:當第一次點選OpenChild選單項時,建立子視窗並正確顯示。不關閉子視窗的情況下再點選OpenChild選單項,子視窗只顯示了一個,說明按預期工作了。現在,我們關閉子視窗,再點選OpenChild選單項,程式在執行到下面這個語句時出現“未處理ObjectDisposedException”異常。
if(myChildFrm != null)
{
    myChildFrm.Show();
//顯示子視窗 }
錯誤資訊:無法訪問已釋放的物件。物件名:“childFrm”。   這就讓人奇怪了。如果子視窗沒有被銷燬,那它就應該能夠正確顯示。點選了關閉子視窗,顯然應該子視窗已經銷燬了,按理myChildFrm等於null,執行的時候應該直接執行else後面的語句塊,為什麼卻進入了滿足myChildFrm!=null的語句塊呢?   其實,這個問題與C#的垃圾回收有關。垃圾回收器管理所有的託管物件,所有需要託管資料的.NET語言(包括C#)都受執行庫的垃圾回收器的制約。垃圾回收器可以確定執行垃圾回收的最佳時間,自動進行垃圾回收。然而垃圾回收的一個產物是:C#物件沒有確定性毀壞。所以會出現子視窗物件已被銷燬,但又不為null,故出現訪問時產生“未處理ObjectDisposedException”異常(來自於“從小處看C#.net垃圾回收”一文)。   如何解決這個題,有人提出:應該應該徹底回收Child所佔的資源。並提供瞭解決方法(請搜尋“從小處看C#.net垃圾回收”檢視相關情況)。   其實,現在我們需要解決的問題僅僅是:子視窗已經被銷燬,但物件卻不為null。只需要對你視窗中的選單點選事件函式進行簡單修改就可以了。
private void OpenChildFrmToolStripMenuItem_Click(object sender, EventArgs e)
{
    if(myChildFrm != null)
    {
          if(myChildFrm.IsDisposed)
                  myChildFrm = new ChildFrm();//如果已經銷燬,則重新建立子視窗物件
          myChildFrm.Show();
          myChildFrm.Focus();
     }
    else
    {
        myChildFrm = new ChildFrm();
        myChildFrm.Show();
        myChildFrm.Focus();
    }
}
前面這是按邏輯的方式進行思考的,顯示子視窗和獲得焦點兩行是重複的,兩個if語句也可以做一下簡化。指定子視窗和父視窗的父子關係。最後的結果是這樣:
private void OpenChildFrmToolStripMenuItem_Click(object sender, EventArgs e)
{
    if(myChildFrm == null || myChildFrm.IsDisposed)
    {  
        myChildFrm = new ChildFrm();
    }
    myChild..MdiParent = this; //建立父子關係

        myChildFrm.Show(); //顯示子視窗
    myChildFrm.Focus();  //子視窗獲得焦點
}
這樣,就能夠如我們如願般呼叫子視窗了。   轉載自CSDN部落格,ID:新城  url: https://blog.csdn.net/c61999/article/details/8153476