tags:

views:

296

answers:

4

Let's say I have a class A which can fire an event called X. Now I have a class B and in a method I get an instance to A and bind the event to a handler in B:

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

I have three questions about this.

  • Is it true that when I now set the reference to the B instance to null, it won't be garbage collected since the garbage collector thinks it's still in use (thus keeping a useless and potentially interfering copy of B in memory).

  • What about when I have another object c (of class C) in which I have a reference to A called a ("this.a = new A()"). Then I call "b.BindEvent(this.a)", and in c I set the reference to a to null ("this.a = null"). Will this keep the copy of A in memory because it's referenced through the event in b?

  • If either or both are true of the above, how can I best circumvent these issues? If I have a whole list of event handlers (say 10 lines like "a.SomeEvent += SomeMethod") should I clean them all up again ("a.SomeEvent -= SomeMethod"). At which time or place in the code should I do these things?

Well it's gotten a bit fuzzy but I'm not sure how to explain in a better way. Please leave a comment if I need to explain something more detailed.

+7  A: 

so: A is the publisher and B is the subscriber?

first bullet: if B is the instance with AEventHandler - then it is still in use, so no, it won't get collected unless the a instance is unreachable.

second bullet: huh? (will read again...) If the A and B instances are both unreachable, they will be garbage collected; the event doesn't matter. If A is reachable, then B will stay alive. However, the event subscription never keeps A alive; it is one way... A can keep B alive, but B doesn't keep A alive. Does that cover it?

third bullet: in most cases, the two things have similar life expentency, so it isn't an issue. It only becomes an issue if the thing publishing the event lives a lot longer than the things with the handlers. In which case, you simply need to religiously clean up after yourself - for example: a.X -= AEventHandler. In particular, static events are evil for this reason.

Marc Gravell
+4  A: 

You should really unbind the event handler before destroying the class instance that it relates to. (Using your code as an exmaple.)

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

I would also ask, why are you setting class variables to null?

ChrisBD
+2  A: 
  1. Correct.
  2. Correct.
  3. Why would you want to circumvent this behaviour? It's how the GC is designed to work. If you need to do some clean up when each object is torn down, use the Dispose pattern.
Christian Hayter
I'm pretty sure 2 is incorrect. Firstly, the event *never* keeps the publisher (`A`) alive - and second, nothing can see `A` anyway... so the data is disconnected and collected.
Marc Gravell
Doh! I stand corrected. :-)
Christian Hayter
+1  A: 

Yes, event are references, if you don't unregister, the object implementing the event handler will not be garbage collected.

You can do this:

Remove all registered event, if A knows when they are not used anymore.

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

Or you unregister in B, if B knows when it doesn't use it anymore. You need to keep a reference to a to be able to unregister.

You could also implement IDisposable.

class B : IDisposable
{
  private A registeredToA;

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

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

This is a breaking change to your code, because B needs always to be disposed.

Stefan Steinegger