Cleaning Up Event Handlers

D

dmeglio

Hello,

I'm aware that when an EventHandler is created, it creates a reference
to the object, therefore preventing GCing.

Therefore, I've been implementing IDisposable in my controls to
cleanup the mess (not a fun thing to retrofit).

Anyway, my question is regarding this issue. When I create controls,
the .NET IDE automatically registers events for me (the "Component
Designer generated code" region of code). So I notice in there
something like:
this.menuItemAudioFile.CheckedChanged += new
System.EventHandler(this.menuItemAudioFile_CheckedChanged);

Am I correct in assuming that if I dynamically create the containing
code (e.g. theCtrl = new MyCtrl() more than once) I need to -= that in
my Dispose function? If that's the case, why doesn't the .NET IDE
automatically -= them in a Dispose function? Is there a reason this is
completely left up to the implementor?

Or maybe I'm totally misunderstanding the issue!

Thanks!
 
N

Nicholas Paldino [.NET/C# MVP]

In the case that you show, where you attach to the CheckChanged event,
it doesn't matter, because it is a circular reference, which GC will pick
up. The form holds a reference to the control, which holds a reference to
the form indirectly through the event.

The only time that you have to worry about this is when you subscribe to
an event on another class, and you don't want that reference to prevent you
from disposing yourself.

A good example of this is when you have a form that is attached to
another form's Closed event. If the the form that is subscribed to the
event closes, the object is still in memory (although Disposed) because of
the subscription to the Closed event on the other form.

However, if all the events and subscriptions are self-contained, in the
sense that they are all on the same form/control and only to each other,
then you don't have to worry about this. It's when your form subscribes to
an event outside of itself (or any class, not just forms for that matter)
that you have to worry.
 
D

dmeglio

In the case that you show, where you attach to the CheckChanged event,
it doesn't matter, because it is a circular reference, which GC will pick
up. The form holds a reference to the control, which holds a reference to
the form indirectly through the event.

The only time that you have to worry about this is when you subscribe to
an event on another class, and you don't want that reference to prevent you
from disposing yourself.

A good example of this is when you have a form that is attached to
another form's Closed event. If the the form that is subscribed to the
event closes, the object is still in memory (although Disposed) because of
the subscription to the Closed event on the other form.

However, if all the events and subscriptions are self-contained, in the
sense that they are all on the same form/control and only to each other,
then you don't have to worry about this. It's when your form subscribes to
an event outside of itself (or any class, not just forms for that matter)
that you have to worry.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)




I'm aware that when an EventHandler is created, it creates a reference
to the object, therefore preventing GCing.
Therefore, I've been implementing IDisposable in my controls to
cleanup the mess (not a fun thing to retrofit).
Anyway, my question is regarding this issue. When I create controls,
the .NET IDE automatically registers events for me (the "Component
Designer generated code" region of code). So I notice in there
something like:
this.menuItemAudioFile.CheckedChanged += new
System.EventHandler(this.menuItemAudioFile_CheckedChanged);
Am I correct in assuming that if I dynamically create the containing
code (e.g. theCtrl = new MyCtrl() more than once) I need to -= that in
my Dispose function? If that's the case, why doesn't the .NET IDE
automatically -= them in a Dispose function? Is there a reason this is
completely left up to the implementor?
Or maybe I'm totally misunderstanding the issue!
Thanks!- Hide quoted text -

- Show quoted text -

So essentially, if an object subscribes to an event of one of it's
children (e.g. an encapsulated object), I'm ok?
 
P

Peter Duniho

I'm aware that when an EventHandler is created, it creates a reference
to the object, therefore preventing GCing.

Therefore, I've been implementing IDisposable in my controls to
cleanup the mess (not a fun thing to retrofit).

I think you're doing something you don't need to do. See below.
Anyway, my question is regarding this issue. When I create controls,
the .NET IDE automatically registers events for me (the "Component
Designer generated code" region of code). So I notice in there
something like:
this.menuItemAudioFile.CheckedChanged += new
System.EventHandler(this.menuItemAudioFile_CheckedChanged);

Am I correct in assuming that if I dynamically create the containing
code (e.g. theCtrl = new MyCtrl() more than once) I need to -= that in
my Dispose function? If that's the case, why doesn't the .NET IDE
automatically -= them in a Dispose function? Is there a reason this is
completely left up to the implementor?

If the class containing the event cannot be collected then any other class
that has subscribed to the event also cannot be collected. This does mean
that if you've got a class that's subscribed to an event but does not
unsubscribe before you try to get rid of it (you have no other explicit
references to it), the class won't be collected. However, if that happens
and then the class with the event is also left unreferenced, then neither
the class with the event or the class subscribed to its event will be
reachable and so both can be collected at that point.

Now, all that said, in your particular example you appear to be dealing
with code in which the class containing the instance with the event is the
one with the handler. So presumably you're not going to be discarding
that class until you've also discarded the instance with the event, and
then everything gets cleaned up fine.

This is the situation with Designer-created forms and controls, which is
why there's no need to do any sort of IDispose stuff to clean things up.
In fact, I can't think of any situation related to event subscription
which would require you to implement a Dispose method. Events are
managed, subscribers are managed, so the GC knows all about them and
should always do the right thing.

Pete
 
N

Nicholas Paldino [.NET/C# MVP]

Yes, you are ok. It creates a circular reference, and when all
references to those individual objects outside of each other are released,
your object will be eligible for GC.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

dmeglio said:
In the case that you show, where you attach to the CheckChanged
event,
it doesn't matter, because it is a circular reference, which GC will pick
up. The form holds a reference to the control, which holds a reference
to
the form indirectly through the event.

The only time that you have to worry about this is when you subscribe
to
an event on another class, and you don't want that reference to prevent
you
from disposing yourself.

A good example of this is when you have a form that is attached to
another form's Closed event. If the the form that is subscribed to the
event closes, the object is still in memory (although Disposed) because
of
the subscription to the Closed event on the other form.

However, if all the events and subscriptions are self-contained, in
the
sense that they are all on the same form/control and only to each other,
then you don't have to worry about this. It's when your form subscribes
to
an event outside of itself (or any class, not just forms for that matter)
that you have to worry.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)




I'm aware that when an EventHandler is created, it creates a reference
to the object, therefore preventing GCing.
Therefore, I've been implementing IDisposable in my controls to
cleanup the mess (not a fun thing to retrofit).
Anyway, my question is regarding this issue. When I create controls,
the .NET IDE automatically registers events for me (the "Component
Designer generated code" region of code). So I notice in there
something like:
this.menuItemAudioFile.CheckedChanged += new
System.EventHandler(this.menuItemAudioFile_CheckedChanged);
Am I correct in assuming that if I dynamically create the containing
code (e.g. theCtrl = new MyCtrl() more than once) I need to -= that in
my Dispose function? If that's the case, why doesn't the .NET IDE
automatically -= them in a Dispose function? Is there a reason this is
completely left up to the implementor?
Or maybe I'm totally misunderstanding the issue!
Thanks!- Hide quoted text -

- Show quoted text -

So essentially, if an object subscribes to an event of one of it's
children (e.g. an encapsulated object), I'm ok?
 
D

dmeglio

Yes, you are ok. It creates a circular reference, and when all
references to those individual objects outside of each other are released,
your object will be eligible for GC.

One more follow up... Say I have Form1. On Form1 is Control1. Now lets
say, every 20 seconds I change Control1 to something else (very
hypothetical, but some pseudocode might help):
every_20_seconds()
{
this.Control1.SomeEvent += new MyHandler
this.Control1 = new Control1(someNewValue);
}

In this instance, when the new Control1 is executed, the "old"
Control1 still exists because I have an event reference to it,
correct? Now it will be disposed when Form1 is disposed, but Form1 is
my main app window, therefore, if I want the "old" Control1s' memory
to be freed, I need to have a -= before I allocate a new Control1?
Hope that made sense :)
 
N

Nicholas Paldino [.NET/C# MVP]

You have it backwards.

When you do this:

this.Control1.SomeEvent += new MyHandler

Then Control1 has a reference to the form (or whatever object has the
method that MyHandler is going to execute). When you do this:

this.Control1 = new Control1(someNewValue);

Then the old Control1 ends up being eligible for garbage collection
(make sure you call Dispose on the old Control1 before you assign a new
control to it). It still has a reference to your form because it has the
delegate, but that doesn't make it eligible for GC, as your form is still
rooted. The form doesn't have a reference to the control anymore.
 
P

Peter Duniho

[...]
In this instance, when the new Control1 is executed, the "old"
Control1 still exists because I have an event reference to it,
correct? Now it will be disposed when Form1 is disposed, but Form1 is
my main app window, therefore, if I want the "old" Control1s' memory
to be freed, I need to have a -= before I allocate a new Control1?

That would be a pretty poor design, destroying and recreating controls on
the fly like that. But yes, assuming you find that design necessary, then
if the form doesn't unsubscribe from the event, the control you've tried
to discard will remain in memory. One hopes that it has no way to fire
its event at that point, but if it should do so, then you will even find
your form's event handler getting called.

Note that even in this case though, implementing Dispose() in the
subscriber to have it unsubscribe doesn't get you anything, because the
subscriber isn't the thing that's being discarded. You need to explicitly
unsubscribe your form's handler from the control before you discard the
control (as you guessed).

Pete
 
N

Nicholas Paldino [.NET/C# MVP]

Peter,

The control will not remain in memory. If the form owns the control,
and the control has a reference back to the form indirectly through an event
handler, then when you release the reference on the control, the control is
eligible for GC, the form has no ties to it anymore.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

[...]
In this instance, when the new Control1 is executed, the "old"
Control1 still exists because I have an event reference to it,
correct? Now it will be disposed when Form1 is disposed, but Form1 is
my main app window, therefore, if I want the "old" Control1s' memory
to be freed, I need to have a -= before I allocate a new Control1?

That would be a pretty poor design, destroying and recreating controls on
the fly like that. But yes, assuming you find that design necessary, then
if the form doesn't unsubscribe from the event, the control you've tried
to discard will remain in memory. One hopes that it has no way to fire
its event at that point, but if it should do so, then you will even find
your form's event handler getting called.

Note that even in this case though, implementing Dispose() in the
subscriber to have it unsubscribe doesn't get you anything, because the
subscriber isn't the thing that's being discarded. You need to explicitly
unsubscribe your form's handler from the control before you discard the
control (as you guessed).

Pete
 
P

Peter Duniho

The control will not remain in memory. If the form owns the control,
and the control has a reference back to the form indirectly through an
event
handler, then when you release the reference on the control, the control
is
eligible for GC, the form has no ties to it anymore.

Right. I knew that. :)

Sorry for any confusion...my post was obviously ill-conceived. :)

Pete
 
D

dmeglio

Let me just ask one more related question, I know that when I'm done
with a modal dialog I should call Dispose (basically after the
ShowDialog call), but in the Form's Dispose method, do I need to call
the Dispose for each of its children controls? Meaning, do I need to
do btnOK.Dispose()?
 
N

Nicholas Paldino [.NET/C# MVP]

No, you do not, calling Dispose will end up calling Dispose on all child
controls contained on the form.
 
D

dmeglio

No, you do not, calling Dispose will end up calling Dispose on all child
controls contained on the form.

That's what I thought, but some Google results are saying the
opposite... maybe I need to make a proof of concept.
--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)




Let me just ask one more related question, I know that when I'm done
with a modal dialog I should call Dispose (basically after the
ShowDialog call), but in the Form's Dispose method, do I need to call
the Dispose for each of its children controls? Meaning, do I need to
do btnOK.Dispose()?- Hide quoted text -

- Show quoted text -
 
N

Nicholas Paldino [.NET/C# MVP]

Easy enough to do:

using (Button b = new Button())
{
b.Text = "Hey there";

using (Form f = new Form())
{
f.Controls.Add(b);
}

bool disposed = b.IsDisposed;
}

"disposed" will be true.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

dmeglio said:
No, you do not, calling Dispose will end up calling Dispose on all
child
controls contained on the form.

That's what I thought, but some Google results are saying the
opposite... maybe I need to make a proof of concept.
--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)




Let me just ask one more related question, I know that when I'm done
with a modal dialog I should call Dispose (basically after the
ShowDialog call), but in the Form's Dispose method, do I need to call
the Dispose for each of its children controls? Meaning, do I need to
do btnOK.Dispose()?- Hide quoted text -

- Show quoted text -
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top