68

If I have an application with only a few event handlers registered (and the objects using the events are not disposed until the application is closed), do I really need to worry about unregistering those handlers? The only good reason I could see is that there might be a little extra overhead if events are being fired that you dont necessarly care about (i.e you have multiple handlers registered to one event). Is there any other good reason to? Anyone run into major issues because they didnt unregister events?

SwDevMan81
  • 48,814
  • 22
  • 151
  • 184

2 Answers2

94

If you have A publishing an event, and B subscribing to an event (the handler), then it is only a problem not to unsubscribe if A is going to live a lot longer than B. Basically, the event subscription means that A can still see B, so would prevent it from being garbage collected, and would still fire events on it even if you've forgotten about it (and perhaps Disposed() it).

For example, this is a problem if A is a static event, and your app runs for a while after B dies... ButB will live as long as A , thus B will not be garbage collected.

It is important to note, one might ask the following:

if B lives a lot longer than A, will B keep A from being garbage collected?

And the answer to that is "no". B has no reference to A through the event; A will be collected as normal

joe
  • 1,078
  • 2
  • 11
  • 30
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Great insight Marc. handlers for static events are really notorious if not taken care while disposing the subscriber. – TheVillageIdiot Jun 30 '09 at 04:37
  • 3
    I know this is a old post and I do not know if it will ever get read but if B lives a lot longer than A, will B keep A from being garbage collected? – Scott Chamberlain Jun 18 '10 at 18:31
  • 6
    @Scott. No - `B` has no reference to `A` through the event; `A` will be collected as normal. – Marc Gravell Jun 18 '10 at 19:12
  • Basically unsubscribe object `B` from `A`, if you destroy object `B` but not object `A` else `A` will invoke to `B` which is null. If you destroy object `A` the events are cleared anyway.... i think ? – WDUK Oct 06 '21 at 06:49
  • @WDUK "else A will invoke to B which is null" - no, B will never become `null` - that's not how GC works; simply: B will not be eligible for collection until A becomes unreachable and eligible for collection – Marc Gravell Oct 06 '21 at 07:33
  • If you do `OnChange?.Invoke();` when you have destroyed B of which subscribed to `OnChange` in `A` you get a null ref error... at least i do... – WDUK Oct 06 '21 at 10:20
  • @WDUK you lack the capacity to destroy an object; there is literally no API that can do that; the *only* thing that can destroy a managed object is the GC, and if object B is subscribed to an event on a reachable object (A), then B is reachable and will not be collected (destroyed). I'm guessing what you mean is "I called close/shutdown/dispose on B" - this is **very** different to destroying the object, but yes: if you mutate the internal state of B such that it now doesn't work: then it now doesn't work. This is a fairly reductive and redundant statement, though. – Marc Gravell Oct 06 '21 at 10:33
  • My point was it will throw an error as a null ref even if GC has not cleaned it up after calling dispose so its pretty hard to make the mistake anyway... unless the delegate is never invoked again. – WDUK Oct 06 '21 at 21:03
  • @WDUK no - nothing in the event subscription will cause a null-ref here; the only thing that will cause a null-ref is internal to the specific type. For example, if you have a `Dispose()` method that closes a file and sets a stream reference to null, and your handler method assumes that the file will be non-null, then: the handler will explode when invoked, but that is specific to the handler and the dispose implementation. Again, to repeat: nothing in the event (delegate) infrastructure, or GC, will cause a null-reference here. That is entirely self-inflicted in your code. – Marc Gravell Oct 07 '21 at 08:14
18

Many people seem to think that it's only important to unsubscribe from events if the publisher is going to outlive the subscriber. I dislike that approach. An event subscriber which does not detach itself from the publisher creates some a nasty dependencies on the behavior of entities outside the publisher and subscriber. If a reference to the publisher is held longer than expected, that will keep the subscriber alive, along with any objects to which the subscriber holds a reference. If a large mass of abandoned objects are interconnected by event handlers, but no live reference exists to any of them, all the objects can be swept up by the garbage collector. If, however, someone somewhere unexpectedly keeps a reference to one of the objects, that may prevent any of them from being garbage-collected.

IMHO, it's much better to be proactive in removing event handlers than to abandon them and hope that everything gets cleaned up. Unless one can be certain that no unexpected references to the publisher can exist, such an approach is likely to 'mostly' work, but cause occasional memory leaks.

supercat
  • 77,689
  • 9
  • 166
  • 211