Should I always disconnect event handlers in the Dispose method?

24,265

Solution 1

Unless you expect the publisher of the event to outlive the subscriber, there's no reason to remove the event handler, no.

This is one of those topics where folk lore has grown up. You really just need to think about it in normal terms: the publisher (e.g. the button) has a reference to the subscriber. If both the publisher and the subscriber will be eligible for garbage collection at the same time anyway (as is common) or if the publisher will be eligible for garbage collection earlier, then there's no GC issue.

Static events cause a GC problem because they're effectively an infinitely-long-lived publisher - I would discourage static events entirely, where possible. (I very rarely find them useful.)

The other possible issue is if you explicitly want to stop listening for events because your object will misbehave if the event is raised (e.g. it will try to write to a closed stream). In that case, yes, you should remove the handler. That's most likely to be in the case where your class implements IDisposable already. It would be unusual - though not impossible - for it to be worth implementing IDisposable just to remove event handlers.

Solution 2

Well, perhaps, the standard was proposed as a defensive practice against memory leaks. I can't say, this is a bad standard. But, I personally prefer to disconnect event handler ONLY where needed. In that way, my code looks clean and less verbose.

I have written a blog explaining how event handler causes a memory leak and when to disconnect; https://www.spicelogic.com/Blog/net-event-handler-memory-leak-16. Here, I will summarize the explanation to address your core question.

C# Event Handler operator is actually a reference injector:

In C# += operator looks very innocent and many new developers do not get the idea that the right-hand side object is actually passing it's a reference to the left-hand side object.

enter image description here

Event publisher protects event subscriber:

So, if an object gets a reference to another object, what is the problem? The problem is that, when the garbage collector comes to clean up and find an object that is important to keep in memory, it will not clean up all objects that are also referenced by that important object. Let me make it simple. Say, you have an object named "Customer". Say, this customer object has a reference to the CustomerRepository object so that the customer object can search the repository for all of its Address objects. So, if the garbage collector finds that the customer object is needed to be alive, then the garbage collector will also keep the customer repository alive, because, the customer object has a reference to the customerRepository object. Which makes sense as the customer object needs the customeRepository object to function.

But, does an event publisher object needs an event handler to function? NO, right? the event publisher is independent of the event subscriber. Event publishers should not care if an event subscriber is alive or not. When you use the += operator to subscribe to an event of an event publisher, the event publisher receives a reference of the event subscriber. The garbage collector thinks, the event publisher needs the event subscriber object to function, so it does not collect the event subscriber object.

In that way, the event publisher object "a" protects the event subscriber object "b" from being collected by the garbage collector.

Event publisher object PROTECTS the event subscriber object as long as the event publisher object is alive.

enter image description here

So, if you detach the event handler, then the event publisher does not hold the reference of the event subscriber, and the garbage collector can freely collect the event subscriber.

But, do you really need to detach the event handler all the time? The answer is No. Because many event subscribers are really supposed to be living in the memory as long as the event publisher lives.

A Flow Chart to make the right decision:

enter image description here

Most of the time, we find the event subscriber object is as important as the event publisher object and both are supposed to be living at the same time.

Example of a scenario where you do not need to worry:

For example, a button click event of a window.

enter image description here

Here, the event publisher is the Button, and the event subscriber is the MainWindow. Applying that flow chart, ask a question, does the Main Window (event subscriber) supposed to be dead before the Button (event publisher)? Obviously No. Right? That won't even make sense. Then, why worry about detaching the click event handler?

An example when an event handler detachment is a MUST:

I will provide one example where the subscriber object is supposed to be dead before the publisher object. Say, your MainWindow publishes an event named "SomethingHappened" and you show a child window from the main window by a button click. The child window subscribes to that event of the main window.

enter image description here

And, the child window subscribes to an event of the Main Window.

enter image description here

When the user clicks a button in a MainWindow, the child window shows up. Then the user closes the child window when he/she finishes the task from the child window. Now, according to the flow chart I provided if you ask a question "Does the child window (event subscriber) supposed to be dead before the event publisher (main window)? The answer should be YES. Right? Then, make sure to detach the event handler when the task of the child window is done. A good place is the Unloaded event of the ChildWindow.

Validating the concept of memory leak:

I have profiled this code using the dotMemory Memory profiler software from Jet Brains. I started the MainWindow and clicked the button 3 times, which shows a child window. So, 3 instances of the Child Window showed up. Then, I have closed all the child windows and compared a snapshot before and after the child window appearance. I found that 3 objects of the Child Window were living in the memory even I have closed all of them.

enter image description here

Then, I have detached the event handler in the Unloaded event of the child window, like this:

enter image description here

Then, I have profiled again, and this time, wow! no more memory leak caused by that event handler.

enter image description here

Solution 3

I had a major GDI leak in my application if I didn't unregister the event handlers in the Dispose() of a user control that was being dynamically created and destroyed. I found the following in the Visual Studio 2013 help, in the C# Programming Guide. Note the stuff I have put in italics:

How to: Subscribe to and Unsubscribe from Events

...snip... Unsubscribing To prevent your event handler from being invoked when the event is raised, unsubscribe from the event. In order to prevent resource leaks, you should unsubscribe from events before you dispose of a subscriber object. Until you unsubscribe from an event, the multicast delegate that underlies the event in the publishing object has a reference to the delegate that encapsulates the subscriber's event handler. As long as the publishing object holds that reference, garbage collection will not delete your subscriber object.

Note that in my case both the publisher and the subscriber were in the same class, and the handlers are not static.

Solution 4

One reason to do it I faced was that it affected assembly unloadability

Share:
24,265
No Idea For Name
Author by

No Idea For Name

working on angularjs, java, mySQL, tomcat, etc. answer with the most effort i gave so far: Reference type in C# the question the intrested me the most: Should I always disconnect event handlers in the Dispose method?

Updated on July 09, 2022

Comments

  • No Idea For Name
    No Idea For Name almost 2 years

    I'm working in C# and my workplace has some code standards. One of them is that each event handler we connect (such as KeyDown) must be disconnected in the Dispose method. Is there any good reason for that?

  • frontsidebus
    frontsidebus over 7 years
    I've found that in game programming static event classes or publishers that outlive subscribers are common.
  • Jon Skeet
    Jon Skeet over 7 years
    @frontsidebus: That may well be a matter of poor design rather than it being inherent in the problem though...
  • frontsidebus
    frontsidebus over 7 years
    A typical use case might be that you load levels in and out of memory as the game progresses but each level needs to be able to emit certain events to the core game framework or other subsystems which have different lifetimes.
  • Jon Skeet
    Jon Skeet over 7 years
    @frontsidebus: That still doesn't sound like something I'd use static events for...
  • frontsidebus
    frontsidebus over 7 years
    I dont mean events that are static themselves but classes that are either static or have only a single instance that lives for the lifetime of the game.
  • Jon Skeet
    Jon Skeet over 7 years
    @frontsidebus: Static classes rarely sound appropriate for that either. But I don't think there's much point continuing this in comments.
  • Chaser324
    Chaser324 about 7 years
    @frontsidebus - I know this is a six month old conversation, but I just want to mention here: Dependency Injection. That's what you want. I understand why you think static classes, singletons, etc. are so necessary, game programming is mired in that thinking, but there are better ways.