清理事件处​​理函数的引用哪些最佳实践? [英] What best practices for cleaning up event handler references?

查看:174
本文介绍了清理事件处​​理函数的引用哪些最佳实践?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常发现自己写这样的代码:

Often I find myself writing code like this:

        if (Session != null)
        {
            Session.KillAllProcesses();
            Session.AllUnitsReady -= Session_AllUnitsReady;
            Session.AllUnitsResultsPublished -= Session_AllUnitsResultsPublished;
            Session.UnitFailed -= Session_UnitFailed;
            Session.SomeUnitsFailed -= Session_SomeUnitsFailed;
            Session.UnitCheckedIn -= Session_UnitCheckedIn;
            UnattachListeners();
        }



其目的是清理我们已经注册上的所有事件订阅目标(会议),以使会话可以自由地通过GC进行处理。我有一个同事约但是,这种实现IDisposable类的讨论,这是他的信念,这些类应该瓶坯清理这样的:

The purpose being to clean up all event subscriptions that we have registered for on the target (Session) so that Session is free to be disposed by the GC. I had a discussion with a co-worker about classes that implement IDisposable however and it was his belief that those classes should preform cleanup like this:

    /// <summary>
    /// Disposes the object
    /// </summary>
    public void Dispose()
    {
        SubmitRequested = null; //frees all references to the SubmitRequested Event
    }



是否有prefering一个理由比其他?有没有更好的办法去了解这个干脆? (除了弱引用事件无处不在)

Is there a reason for prefering one over the other? Is there a better way to go about this altogether? (Aside from weak reference events everywhere)

我真的很想看到的是somethign类似安全调用模式为筹款活动:即安全的和可重复的。这是我能记得做每次我重视的事件,这样我可以保证这将是容易的,我收拾。

What I'd really like to see is somethign akin to the safe invocation pattern for raising events: i.e. safe and repeatable. Something I can remember to do everytime I attach to an event so that I can ensure it will be easy for me to clean up.

推荐答案

这是不正确的说,从会话事件注销处理程序会以某种方式允许收集会话对象通过GC进行。下面的示例说明事件的参考链的示意图。

It is incorrect to say that unregistering the handlers from the Session events will somehow allow a Session object to be collected by the GC. Here is a diagram that illustrates the reference chain of events.

--------------      ------------      ----------------
|            |      |          |      |              |
|Event Source|  ==> | Delegate |  ==> | Event Target |
|            |      |          |      |              |
--------------      ------------      ----------------

所以你的情况事件源是一个会话对象。但我没有看到你所提到的类中声明的处理程序,所以我们还不知道事件的目标是谁。让我们考虑两种可能性。本次活动的目标可能是相同的会话对象表示源或者它可能是一个完全独立的类。在这两种情况下,在正常情况下会话将只要没有到,即使处理其事件保持注册另一个参考收集。这是因为该委托不包含返回参考事件源。它只包含对事件目标的引用。

So in your case the event source is a Session object. But I do not see that you mentioned which class declared the handlers so we do not yet known who the event target is. Lets consider two possibilities. The event target could be the same Session object that represents the source or it could be an entirely separate class. In either case and under normal circumstances the Session will be collected as long as there is not another reference to even if the handlers to its events remain registered. That is because the delegate does not contain a reference back to the event source. It only contains a reference to the event target.

考虑下面的代码。

public static void Main()
{
  var test1 = new Source();
  test1.Event += (sender, args) => { Console.WriteLine("Hello World"); };
  test1 = null;
  GC.Collect();
  GC.WaitForPendingFinalizers();

  var test2 = new Source();
  test2.Event += test2.Handler;
  test2 = null;
  GC.Collect();
  GC.WaitForPendingFinalizers();
}

public class Source()
{
  public event EventHandler Event;

  ~Source() { Console.WriteLine("disposed"); }

  public void Handler(object sender, EventArgs args) { }
}

您将看到,处置被打印两次到控制台核实收集两个实例不注销事件。通过测试2 引用的对象被收集的原因是因为它仍然在参考图中一个孤立的实体(一旦测试2 是设置为null即是),即使它有一个引用回自己,虽然该事件。

You will see that "disposed" is printed twice to the console verifying that both instances were collected without unregistering the event. The reason the object referenced by test2 gets collected is because it remains an isolated entity in the reference graph (once test2 is set to null that is) even though it has a reference back to itself though the event.

现在,事情变得棘手的是,当你想有事件目标有寿命比所述事件源短。在这种情况下,你的有无的注销事件。考虑下面的代码演示了这一点。

Now, where things get tricky is when you want to have the event target have a lifetime that is shorter than the event source. In that case you have to unregister the events. Consider the following code that demonstrates this.

public static void Main()
{
  var parent = new Parent();
  parent.CreateChild();
  parent.DestroyChild();
  GC.Collect();
  GC.WaitForPendingFinalizers();
}

public class Child
{
  public Child(Parent parent)
  {
    parent.Event += this.Handler;
  }

  private void Handler(object sender, EventArgs args) { }

  ~Child() { Console.WriteLine("disposed"); }
}

public class Parent
{
  public event EventHandler Event;

  private Child m_Child;

  public void CreateChild()
  {
    m_Child = new Child(this);
  }

  public void DestroyChild()
  {
    m_Child = null;
  }
}

您将看到,处置从不打印到控制台显示出可能的内存泄漏。这是一个特别困难的处理问题。实施的IDisposable 儿童,因为没有一种机制保障呼叫者会发挥很好,实际上叫<不能解决问题code>的Dispose 。

You will see that "disposed" is never printed to the console demonstrating a possible memory leak. This is a particularly difficult problem to deal with. Implementing IDisposable in Child will not solve the problem because there is no guarentee that callers will play nicely and actually call Dispose.

答案

如果您的事件源实现的IDisposable 那么你有没有真的买自己什么新的东西。这是因为,如果该事件源不再源于比事件目标将不再是植根为好。

If your event source implements IDisposable then you have not really bought yourself anything new. That is because if the event source is no longer rooted than the event target will no longer be rooted as well.

如果您的活动目标农具 IDisposable的那么它可以从事件源清除本身,而是没有一种机制保障了的Dispose 将被调用。

If your event target implements IDisposable then it could clear itself from the event source but there is no guarentee that Dispose will get called.

我不是说从的Dispose 即注销事件是错误的。我的意思是,你真的需要检查你的类​​层次结构是如何定义的,并考虑如何能最好地避免了内存泄漏问题,如果存在的话。

I am not saying that unregistering events from Dispose is wrong. My point is that you really need to examine how your class hierarchy is defined and consider how you might best avoid the memory leak problem if one even exists.

这篇关于清理事件处​​理函数的引用哪些最佳实践?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆