Should I always disconnect event handlers in the Dispose method?
Asked Answered
U

4

58

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?

Ultan answered 1/7, 2013 at 8:15 Comment(5)
It certainly makes sense if the events are static, since otherwise, the handler reference will serve to root the object and prevent GC.Klaxon
This sounds like a case where someone once had a memory leak caused by otherwise incorrect code, traced the problem to an event handler leak, and decided to write and enforce a global code standard that all event handlers must be disconnected in the Dispose method. In other words, instead of actually working to understand what caused the actual problem in that specific case, they decided to make more work for everyone everafter. A perverse form of cargo-cult programming, except one only the boss can inflict.Straightjacket
Don't know how in latest versions of .NET but on 2.0 and 3.5 we had actually issues on non static event subscriptions, whe use in massive amounf of objects, or used frequently. Seemed like if that GC wasn't able to figure out dead links and memory pumps. Repeat, have not any scientific proof of that, but some real world practise, met and confirmed also from other members of community.Tonguelash
@CodyGray you are absolutely correct, there were memory leak just before they started enforcing this standardUltan
@CodyGray: Aside from the dismal failure of .NET languages to facilitate it, is there any reason why event unsubscription shouldn't be done as a matter of course? If e.g. a control subscribes to some events from the parent and doesn't unsubscribe them, that control is assuming that the number of instances that will be created over the parent's lifetime will be bounded. If a parent periodically creates and destroys child controls, that could cause an unbounded memory leak. Handling event cleanup consistently should be easier than trying to identify those cases that will fail without it.Rubinrubina
T
98

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.

Thurmanthurmann answered 1/7, 2013 at 8:19 Comment(10)
I've found that in game programming static event classes or publishers that outlive subscribers are common.Haslam
@frontsidebus: That may well be a matter of poor design rather than it being inherent in the problem though...Thurmanthurmann
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.Haslam
@frontsidebus: That still doesn't sound like something I'd use static events for...Thurmanthurmann
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.Haslam
@frontsidebus: Static classes rarely sound appropriate for that either. But I don't think there's much point continuing this in comments.Thurmanthurmann
@Haslam - 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.Bifoliolate
Took google over 8 years to get me to this answer. Life would have been so much easier otherwise.Ancel
But question is: Is GC clever enough to dispose the event publisher before the event subscriber?Magnetoelectricity
@Alireza: Potentially - because the publisher has a reference to the subscriber, but not the other way round. (If you're still curious, I suggest you ask a new question with a concrete example.)Thurmanthurmann
T
29

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

Toscana answered 18/5, 2020 at 8:52 Comment(1)
This is one of the best detailed descriptions I read on StackOverflow!Gerdi
J
11

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.

Jurywoman answered 19/5, 2016 at 10:16 Comment(0)
D
2

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

Drongo answered 5/3, 2021 at 17:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.