(using vb.net)
Hi,
In a class, I have a public shared event.
In my form, I have a usercontrol which I "AddHandler" to the shared event. But to avoid memory leaks (since the shared event never gets disposed as long as the application runs) how can I tell my usercontrol to RemoveHandler?
I tried to use the "Disposed" event, but it doesn't always seams like the usercontrol is disposed right away. I want to RemoveHandler as soon as the parent form closes.
What is best practice?
EventHandlerList might be what you need - I use it to remove Click events from Buttons when some of my other controls have updated
http://msdn.microsoft.com/en-us/library/system.componentmodel.eventhandlerlist.removehandler.aspx
Related
At work, my old boss would insist we programmatically assign and remove event handlers from our controls in the code behind, rather than simply double clicking a button (for example).
We'd have an AttachEvents() and DetachEvents() method on every single form. I don't remember his explanation as to why this is allegedly better than assigning it from the Design View, and he's since been transferred to a different project. My new manager doesn't know.
I thought he said it had something to do with events not properly being removed from memory, but I really don't know.
So: What is the benefit of doing it this way?
Performance-wise they are the same.
But doing it in the code behind is a much neater way since you control when to AttachEvents() or DetachEvents()
But you have to be careful in terms of avoiding any duplicate event wire ups. These in turn might lead up eating memory, but prominently they would cause performance issues since the event handler would be called as many times as it was wired up.
Some event handlers such as timers need to be removed before leaving a form otherwise they would still fire, for example
timer.Elapsed += ...
and
timer.Elapse -= ...
Probably a good and clear way to remember to do it in this way.
Other than the implications of your own architecture, there's no difference whatsoever.
The designer will place the event subscriptions on the InitializeComponent method and you should not do any changes to that method because the designer might override them or crash if it's something it can't handle.
Usually, the event subscriptions are events the form subscribes from its children. When the from is closed/disposed, all the children are disposed and each child will dispose event subscriptions.
Problems arise when the form subscribes to outside components. Then the form becomes "attached" to those components and, if not unsubscribed, becomes a resource leak.
There's also the possibility of events being fired when the form is not ready to handle them.
I've used a mixed approach where events from child controls were subscribed in the designer (or carefully manually coded in the InitializeComponent method) and used the AttachEvents/DetachEvents approach for components outside the UI (or the scope of the form/control).
I have a program which allows the editing of product information. I noticed that it was not releasing memory after closing the editing forms. After some research I stumbled upon this question which mentions that the problem may be that it is hanging on to event subscriptions.
That made sense to me because this form has about 100+ controls on it, many of which are custom with custom events which are subscribed to by their parent controls. This creates a pretty large hierarchy of event subscriptions. So I looked for a way to release these and found this which allows you to unsubscribe from that event.
The problem is, I have a ton of subscriptions. Do I really have to manually unsubscribe from each event one by one on form close, or is there at least a way to release them in one fell swoop, or loop through them?
Remember this: The object on the LEFT of the += keeps alive the object containing the method on the RIGHT of the +=. That is, the object that raises the event keeps alive the object that handles the event, even if the object (such as a form) that handles the event is disposed.
So the thing you need to ensure is that all the event-raisers have gone away.
However, if all the event-raisers happen to be controls in the same Form class that subscribes to those events, you will not need to manually unhook all the event handlers when the form is closed.
This is because the controls that raise the events to which to form has subscribed have the same lifetime as the form itself.
You only need to worry if you subscribe to events raised by an object that has a longer lifetime than the object that is subscribing. Then the subscribing object (the form) would need to unsubscribe when it (the form) is disposed.
It depends on how long your form and its events will be living.
However, you can loop through your controls within the form, releasing the events.
If you remove a nonexisting event accidentally - don't worry, it won't throw an exception.
For example, this is how to get rid of all your TextBox.KeyDown-Events:
private void frm_FormClosed(object sender, FormClosedEventArgs e)
{
foreach (Control tb in this.Controls)
{
if (tb is TextBox)
{
TextBox tb1 = (TextBox)tb;
tb1.KeyDown -= TextBox_KeyDown;
}
}
Okay, make an example here:
I have UserControl A, UserControl B, UserControl C and one Windows Form.
This Windows Form is only started with UserControl A.
UserControl C has [Next] and [Back] buttons.
Say, UserControl A is declared with an event handler. One of function in UserControl A will actually raise the event call to execute one function at UserControl C.
So, at UserControl C, I have to add with
"UserControlA.OneFunction += this.UserControlC_Function;"
If I click Next button at UserControl C, it will dispose the UserControl A and add new UserControl B to the Windows Form. But I never remove this event handler manually.
One of the function in UserControl A is the caller (where event is declared).
One of the function in UserControl C is the listener.
So, these are my questions:
Should I manually remove the handler before UserControl A disposed?
Will this User Control A dispose automatically remove the handler that declared previously?
Should I add this somewhere?
"UserControlA.OneFunction -= this.UserControlC_Function;"
By convention, we don't. And since no event should be invoked after disposal, there is no need to do so unless the control in question is behaving weirdly.
No. At least there isn't such code as seen from reflector.
You don't need to remove the handlers in this case because neither the form nor its buttons are referenced by code external to the form, and the entire object graph will therefore be garbage collected.
The answer to this post does a really good job explaining when you need to manually remove an event handler and when it is not necessary.
Do I need to remove event subscriptions from objects before they are orphaned?
If the form is released (assuming no other objects has a reference to the objects in question) there's little risk of not removing the event handler, however it's a good idea always to remove the event handler before the object listening can no longer be reach (ie all variables referencing the object i sout of scope) not doing so can create a memory leak.
This is not the case in your situation (if I get what you are describing, code would make it more clear)
The problem would be if you attach a delegate referencing object C to an event on object A and then looses access to C (e.g. assigning a new value to the variable). C would then hang around until A is garbage collected
If the memory lifetime of an event publisher is not limited relative to the useful lifetime of an event subscriber, failure to unsubscribe an event will likely cause a memory leak. Were it not for the unfortunate hassle of doing so, there wouldn't be any reason for an event subscriber that was being disposed not to unsubscribe from all events, and for an event publisher that was being disposed not to nullify all event subscriptions. Since neither C# nor VB provides any convenient means of doing those things, however, one has to balance the hassle of proper subscription handling with the fact that in many situations one can get away skimping on it.
I have an application with one main form. In the form I have one object objC of class C. The form gets from objC my control and puts it into panel. The form interacts with objC through methods calling and subscribing to objC’s events.
When I try to close the form by clicking on [X] button or by calling this.Close(), form isn’t closing. It calls handler of FormClosing. In the handler, I call objC.Dispose(). I checked, there are no exceptions generated. In objC.Dispose() I unsubscribe from all form’s event handlers. And I removed my control from panel with this code:
splitContainerMain.Panel2.SuspendLayout();
{
splitContainerMain.Panel2.Controls.Clear();
}
splitContainerMain.Panel2.ResumeLayout();
But it just won’t close. I can try to close as many times as I will, FormClosing event will be repeated, but FormClose will be never generated.
This bug is not reproduced when I don't create a control and add it to the panel. What have I done wrong?
There are few explanations for this. But one, you might have a Validating event handler that is canceling the validation. This will also cancel OnFormClosing. Fix:
void Form1_FormClosing(object sender, FormClosingEventArgs e) {
e.Cancel = false;
}
Btw, there is no point in calling Suspend/ResumeLayout, there is no layout done at form closing time. And calling Controls.Clear() does not actually dispose the controls. Rather nasty behavior that trips up a lot of programmers. Best thing to do is to do nothing, parent controls automatically dispose their children controls. And there is no point in unsubscribing events either, the form object and the objC object only reference each other, no other references exist. The garbage collector knows how to handle that.
This question seems to be releated to : Cleaning objects off a form, Where and When?
which provides two good alternatives for dealing with your object.
Either dispose of it by implementing the Form's Dispose method or
Simply ensure that the control is added to the System.ComponentModel.IContainer components property of the form. This will ensure that it's dispose method will be called when the form is disposed.
I just came to realize, by reading some questions and answers on StackOverflow, that adding event handlers using += in C# (or i guess, other .net languages) can cause common memory leaks...
I have used event handlers like this in the past many times, and never realized that they can cause, or have caused, memory leaks in my applications.
How does this work (meaning, why does this actually cause a memory leak) ?
How can I fix this problem ? Is using -= to the same event handler enough ?
Are there common design patterns or best practices for handling situations like this ?
Example : How am I supposed to handle an application that has many different threads, using many different event handlers to raise several events on the UI ?
Are there any good and simple ways to monitor this efficiently in an already built big application?
The cause is simple to explain: while an event handler is subscribed, the publisher of the event holds a reference to the subscriber via the event handler delegate (assuming the delegate is an instance method).
If the publisher lives longer than the subscriber, then it will keep the subscriber alive even when there are no other references to the subscriber.
If you unsubscribe from the event with an equal handler, then yes, that will remove the handler and the possible leak. However, in my experience this is rarely actually a problem - because typically I find that the publisher and subscriber have roughly equal lifetimes anyway.
It is a possible cause... but in my experience it's rather over-hyped. Your mileage may vary, of course... you just need to be careful.
I have explained this confusion in a blog at https://www.spicelogic.com/Blog/net-event-handler-memory-leak-16. I will try to summarize it here so that you can have a clear idea.
Reference means, "Need":
First of all, you need to understand that, if object A holds a reference to object B, then, it will mean, object A needs object B to function, right? So, the garbage collector won't collect object B as long as object A is alive in the memory.
+= Means, injecting reference of Right side object to the left object:
The confusion comes from the C# += operator. This operator does not clearly tell the developer that, the right-hand side of this operator is actually injecting a reference to the left-hand side object.
And by doing so, object A thinks, it needs object B, even though, from your perspective, object A should not care if object B lives or not. As object A thinks object B is needed, object A protects object B from the garbage collector as long as object A is alive. But, if you did not want that protection given to the event subscriber object, then, you can say, a memory leak occurred. To emphasize this statement, let me clarify that, in the .NET world, there is no concept of memory leak like a typical C++ unmanaged program. But, as I said, object A protects object B from garbage collection and if that was not your intention, then you can say a memory leak happened because object B was not supposed to be living in the memory.
You can avoid such a leak by detaching the event handler.
How to make a decision?
There are lots of events and event handlers in your whole code-base. Does it mean, you need to keep detaching event handlers everywhere? The answer is No. If you had to do so, your codebase will be really ugly with verbose.
You can rather follow a simple flow chart to determine if a detaching event handler is necessary or not.
Most of the time, you may 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.
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.
And, the child window subscribes to an event of the Main Window.
From this code, we can clearly understand that there is a button in the Main Window. Clicking that button shows a Child Window. The child window listens to an event from the main window. After doing something, the user closes 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? So, detach the event handler. I usually do that from the Unloaded event of the Window.
A rule of thumb: If your view (i.e. WPF, WinForm, UWP, Xamarin Form, etc.) subscribes to an event of a ViewModel, always remember to detach the event handler. Because a ViewModel usually lives longer than a view. So, if the ViewModel is not destroyed, any view that subscribed event of that ViewModel will stay in memory, which is not good.
Proof of the concept using a memory profiler.
It won't be much fun if we cannot validate the concept with a memory profiler. I have used JetBrain dotMemory profiler in this experiment.
First, I have run the MainWindow, which shows up like this:
Then, I took a memory snapshot. Then I clicked the button 3 times. Three child windows showed up. I have closed all of those child windows and clicked the Force GC button in the dotMemory profiler to ensure that the Garbage Collector is called. Then, I took another memory snapshot and compared it. Behold! our fear was true. The Child Window was not collected by the Garbage collector even after they were closed. Not only that but the leaked object count for the ChildWindow object is also shown as "3" (I clicked the button 3 times to show 3 child windows).
Ok, then, I detached the event handler as shown below.
Then, I have performed the same steps and checked the memory profiler. This time, wow! no more memory leak.
Yes, -= is enough, However, it could be quite hard to keep track of every event assigned, ever. (for detail, see Jon's post). Concerning design pattern, have a look at the weak event pattern.
An event is really a linked list of event handlers
When you do += new EventHandler on the event it doesn’t really matter if this particular function has been added as a listener before, it will get added once per +=.
When the event is raised it go through the linked list, item by item and call all the methods (event handlers) added to this list, this is why the event handlers are still called even when the pages are no longer running as long as they are alive (rooted), and they will be alive as long as they are hooked up. So they will get called until the eventhandler is unhooked with a -= new EventHandler.
See Here
and MSDN HERE
I can tell you that this might possibly become an issue in Blazor. You can have a Component subscribing to events using the += syntax and in the long run, this will cause leaks.
The only solution to this (that I'm aware of) is to not use anonymous methods, have the Component inherit from IDisposable and use Dispose() to unsubscribe the event handler.