如何安全地摆脱事件处理程序?

时间:2009-06-05 11:41:09

标签: c# events

假设我有一个类A,它可以触发一个名为X的事件。现在我有一个B类,在一个方法中我得到一个实例到A并将事件绑定到B中的一个处理程序:

public void BindEvent(A a)
{
    a.X += AEventHandler;
}

我有三个问题。

  • 当我现在将对B实例的引用设置为null时,它不会被垃圾收集,因为垃圾收集器认为它仍然在使用(因此保留了一个无用且可能干扰的B副本)在记忆中)。

  • 当我有另一个对象c(C类)时,我有一个名为a的引用(“this.a = new A()”)。然后我调用“b.BindEvent(this.a)”,在c中我将a的引用设置为null(“this.a = null”)。这是否会将A的副本保留在内存中,因为它是通过b?

  • 中的事件引用的
  • 如果上述情况中的任何一个或两个都成立,我怎样才能最好地规避这些问题?如果我有一个事件处理程序的完整列表(比如“a.SomeEvent + = SomeMethod”这样的10行),我应该再次清理它们(“a.SomeEvent - = SomeMethod”)。我应该在代码中的哪个时间或地点做这些事情?

嗯,它有点模糊,但我不确定如何以更好的方式解释。如果我需要解释一些更详细的内容,请发表评论。

4 个答案:

答案 0 :(得分:8)

所以:A是发布商,B是订阅者吗?

第一个项目符号:如果BAEventHandler的实例 - 那么 仍然在使用中,所以不会,除非{{1实例无法访问。

第二个子弹:嗯? (将再次阅读...)如果aA实例都无法访问,它们将被垃圾收集;事件没关系。如果B可以访问,那么A将保持活跃状态​​。但是,事件订阅从不使B保持活跃状态​​;这是一种方式...... A可以让A保持活跃,但B不会让B保持活着。这涵盖了吗?

第三个问题:在大多数情况下,这两件事具有相似的生活费用,所以这不是问题。如果发布事件的事物比处理事件的事情长得多,那么它只会成为一个问题。在这种情况下,您只需要在自己之后进行宗教清理 - 例如:A。特别是,由于这个原因,a.X -= AEventHandler事件是邪恶

答案 1 :(得分:4)

在销毁与之相关的类实例之前,您应该真正取消绑定事件处理程序。 (使用您的代码作为例子。)

public void UnbindEvent(A a)
{
    a.X -= AEventHandler;
}

我也会问,为什么要将类变量设置为null?

答案 2 :(得分:2)

  1. 正确。
  2. 正确。
  3. 你为什么要绕开这种行为?这就是GC的设计方式。如果在拆除每个对象时需要进行一些清理,请使用Dispose模式。

答案 3 :(得分:1)

是的,event是引用,如果你不注销,实现事件处理程序的对象将不会被垃圾回收。

你可以这样做:

删除所有已注册的活动,如果A知道他们何时不再使用。

class A
{
  // clearing all registrations
  private void ClearEvents()
  {
    X = null;
  }
}

或者你在B中取消注册,如果B知道它何时不再使用它。您需要保留对a的引用才能取消注册。

您还可以实现IDisposable。

class B : IDisposable
{
  private A registeredToA;

  public void BindEvent(A a)
  {
    registeredToA = a;
    registeredToA.X += AEventHandler;
  }

  public void Dispose()
  {
    registeredToA.x -= AEventHandler;
  } 
}

这是对代码的重大更改,因为B总是需要处理。