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 || myChildFrm.IsDisposed)    
    {         
         myChildFrm = new ChildFrm();    
    }    
    myChild..MdiParent = this; //建立父子關係        
    myChildFrm.Show(); //顯示子窗口    
    myChildFrm.Focus();  //子窗口獲得焦點
}

這樣,就能夠如我們如願般調用子窗口了。

 

 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章