Should I always disconnect event handlers in the Dispose method?

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


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.