Often I find myself writing code like this:
if (Session != null)
{
Session.KillAllProcesses();
Session.AllUnitsReady -= Session_AllUnitsReady;
Session.AllUnitsResultsPublished -= Session_AllUnitsResultsPublished;
Session.UnitFailed -= Session_UnitFailed;
Session.SomeUnitsFailed -= Session_SomeUnitsFailed;
Session.UnitCheckedIn -= Session_UnitCheckedIn;
UnattachListeners();
}
The purpose being to clean up all event subscriptions that we have registered for on the target (Session) so that Session is free to be disposed by the GC. I had a discussion with a co-worker about classes that implement IDisposable however and it was his belief that those classes should preform cleanup like this:
/// <summary>
/// Disposes the object
/// </summary>
public void Dispose()
{
SubmitRequested = null; //frees all references to the SubmitRequested Event
}
Is there a reason for prefering one over the other? Is there a better way to go about this altogether? (Aside from weak reference events everywhere)
What I’d really like to see is somethign akin to the safe invocation pattern for raising events: i.e. safe and repeatable. Something I can remember to do everytime I attach to an event so that I can ensure it will be easy for me to clean up.
It is incorrect to say that unregistering the handlers from the
Sessionevents will somehow allow aSessionobject to be collected by the GC. Here is a diagram that illustrates the reference chain of events.So in your case the event source is a
Sessionobject. But I do not see that you mentioned which class declared the handlers so we do not yet known who the event target is. Lets consider two possibilities. The event target could be the sameSessionobject that represents the source or it could be an entirely separate class. In either case and under normal circumstances theSessionwill be collected as long as there is not another reference to even if the handlers to its events remain registered. That is because the delegate does not contain a reference back to the event source. It only contains a reference to the event target.Consider the following code.
You will see that “disposed” is printed twice to the console verifying that both instances were collected without unregistering the event. The reason the object referenced by
test2gets collected is because it remains an isolated entity in the reference graph (oncetest2is set to null that is) even though it has a reference back to itself though the event.Now, where things get tricky is when you want to have the event target have a lifetime that is shorter than the event source. In that case you have to unregister the events. Consider the following code that demonstrates this.
You will see that “disposed” is never printed to the console demonstrating a possible memory leak. This is a particularly difficult problem to deal with. Implementing
IDisposableinChildwill not solve the problem because there is no guarentee that callers will play nicely and actually callDispose.The Answer
If your event source implements
IDisposablethen you have not really bought yourself anything new. That is because if the event source is no longer rooted than the event target will no longer be rooted as well.If your event target implements
IDisposablethen it could clear itself from the event source but there is no guarentee thatDisposewill get called.I am not saying that unregistering events from
Disposeis wrong. My point is that you really need to examine how your class hierarchy is defined and consider how you might best avoid the memory leak problem if one even exists.