首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

從實例理解 C#.NET 垃圾回收:子視窗關閉後重開發生 ObjectDisposedException 的根因與解決

     編輯:關於C語言

問題現場

主視窗透過單例模式開啟子視窗:


// 子視窗單例
public partial class ChildForm : Form
{
    static ChildForm instance;
    public static ChildForm Create()
    {
        if (instance == null) instance = new ChildForm();
        return instance;
    }
}

// 主視窗選單點擊
private void menuItem_Click(object sender, EventArgs e)
{
    ChildForm child = ChildForm.Create();
    child.Show();
    child.Focus();
}

首次開啟正常;關閉子視窗後再次點選選單 → 拋出 ObjectDisposedException。

根因分析:非確定性銷毀與靜態參考殘留

  • GC 非確定性:.NET 垃圾回收器決定何時回收記憶體,Form.Close() 僅釋放非託管資源(視窗控制代碼),不會 將靜態欄位 instance 設為 null。
  • 已釋放物件被重用:instance 仍指向已 Dispose 的 ChildForm 實例,再次呼叫 Show() 時嘗試操作已釋放的控制代碼,觸發例外。

解決方案:在釋放時清除靜態參考

方法一:訂閱 Disposed 事件


public ChildForm()
{
    InitializeComponent();
    this.Disposed += (s, e) => { instance = null; };
}

方法二:覆寫 OnClosed / OnFormClosed


protected override void OnFormClosed(FormClosedEventArgs e)
{
    base.OnFormClosed(e);
    instance = null; // 關鍵:清除單例參考
}

OnFormClosed 在視窗完全關閉、資源釋放後觸發,比 OnClosed 更適合。

進階建議:避免單例模式用於視窗

  • 視窗屬於短生命週期 UI 物件,適合每次新建實例(new ChildForm().Show())。
  • 若需限制單一實例,改用應用程式層級追蹤(如 Application.OpenForms 檢查)而非靜態欄位持有已釋放物件。

補充:GC 關鍵概念速記

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
概念說明
代Gen 0 (短命)、Gen 1、Gen 2 (長命),分代回收提升效能
大型物件堆積 (LOH)>= 85,000 bytes 物件直接配置於 LOH,僅 Gen 2 回收
Finalize / DisposeFinalize 非確定性;Dispose (IDisposable) 確定性釋放非託管資源
GC.Collect()強制回收,生產環境極少主動呼叫