Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>It is incorrect to say that unregistering the handlers from the <code>Session</code> events will somehow allow a <code>Session</code> object to be collected by the GC. Here is a diagram that illustrates the reference chain of events.</p> <pre><code>-------------- ------------ ---------------- | | | | | | |Event Source| ==&gt; | Delegate | ==&gt; | Event Target | | | | | | | -------------- ------------ ---------------- </code></pre> <p>So in your case the event source is a <code>Session</code> object. 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 same <code>Session</code> object that represents the source or it could be an entirely separate class. In either case and under normal circumstances the <code>Session</code> will 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.</p> <p>Consider the following code.</p> <pre><code>public static void Main() { var test1 = new Source(); test1.Event += (sender, args) =&gt; { Console.WriteLine("Hello World"); }; test1 = null; GC.Collect(); GC.WaitForPendingFinalizers(); var test2 = new Source(); test2.Event += test2.Handler; test2 = null; GC.Collect(); GC.WaitForPendingFinalizers(); } public class Source() { public event EventHandler Event; ~Source() { Console.WriteLine("disposed"); } public void Handler(object sender, EventArgs args) { } } </code></pre> <p>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 <code>test2</code> gets collected is because it remains an isolated entity in the reference graph (once <code>test2</code> is set to null that is) even though it has a reference back to itself though the event.</p> <p>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 <em>have</em> to unregister the events. Consider the following code that demonstrates this.</p> <pre><code>public static void Main() { var parent = new Parent(); parent.CreateChild(); parent.DestroyChild(); GC.Collect(); GC.WaitForPendingFinalizers(); } public class Child { public Child(Parent parent) { parent.Event += this.Handler; } private void Handler(object sender, EventArgs args) { } ~Child() { Console.WriteLine("disposed"); } } public class Parent { public event EventHandler Event; private Child m_Child; public void CreateChild() { m_Child = new Child(this); } public void DestroyChild() { m_Child = null; } } </code></pre> <p>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 <code>IDisposable</code> in <code>Child</code> will not solve the problem because there is no guarentee that callers will play nicely and actually call <code>Dispose</code>.</p> <p><strong>The Answer</strong></p> <p>If your event source implements <code>IDisposable</code> then 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.</p> <p>If your event target implements <code>IDisposable</code> then it could clear itself from the event source but there is no guarentee that <code>Dispose</code> will get called.</p> <p>I am not saying that unregistering events from <code>Dispose</code> is 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.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload