NO IMAGE

首先看一段模擬程式碼

  class EnterpriseComponent
  {
    public event EventHandler OnEvent;

    ~EnterpriseComponent()
    {
      Console.WriteLine(“EnterpriseComponent Destroy…”);
    }

    public void DoEvent()
    {
      if (OnEvent != null)
        OnEvent(this, EventArgs.Empty);
    }
  }

  class Client
  {
    ~Client()
    {
      Console.WriteLine(“Client Destroy…”);
    }

    public void OnEvent(object sender, EventArgs e)
    {
      Console.WriteLine(“Event…”);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      EnterpriseComponent eco = new EnterpriseComponent();
      Client client = new Client();

      // 註冊事件
      eco.OnEvent = new EventHandler(client.OnEvent);

      // 執行事件
      eco.DoEvent();

      // 模擬Client超出作用範圍,等待垃圾回收。
      client = null;

      // 執行垃圾回收
      Console.WriteLine(“–GC Strat —————“);
      GC.Collect();
      Console.WriteLine(“–GC End——————“);
    }
  }

輸出:

Event…
–GC Strat —————
–GC End——————

執行這段程式碼,我們可以看出,Client並沒有被垃圾回收。
接下來,我們新增一行程式碼,再來一次。

    static void Main(string[] args)
    {
      EnterpriseComponent eco = new EnterpriseComponent();
      Client client = new Client();

      // 註冊事件
      eco.OnEvent = new EventHandler(client.OnEvent);

      // 執行事件
      eco.DoEvent();

      eco.OnEvent -= new EventHandler(client.OnEvent); // <—– 新增的程式碼,取消事件註冊!!!

      // 模擬Client超出作用範圍,等待垃圾回收。
      client = null;

      // 執行垃圾回收
      Console.WriteLine(“–GC Strat —————“);
      GC.Collect();
      Console.WriteLine(“–GC End——————“);
    }

輸出:

Event…
–GC Strat —————
Client Destroy…
–GC End——————

可以看出,Client在取消事件註冊後被GC回收。其原因就是如果沒有取消事件註冊,那麼委託連結串列中的強引用就不會失效,因此GC也就無法回收。相關細節可以參考SDK中有關事件和委託的說明,在此不做詳述。

注意:只要一個物件仍然登記有另一個物件的事件,該物件就不可能被執行垃圾回收。建議配合IDisposeable介面,在物件失效前取消事件註冊,否則可能造成潛在的記憶體洩漏。