事件注册与潜在隐患

来源:互联网 发布:淘宝小号怎么找回来 编辑:程序博客网 时间:2024/04/30 14:47
首先看一段模拟代码
  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接口,在对象失效前取消事件注册,否则可能造成潜在的内存泄漏。