Note that there are some explanatory texts on larger screens.

plurals
  1. POC# WeakReference object is NULL in finalizer although still strongly referenced
    text
    copied!<p>Hi I have code here where I don't understand why I hit the breakpoint (see comment).</p> <p>Is this a Microsoft bug of something I don't know or I don't understand properly ?</p> <p>The code was tested in Debug but I think it should not changes anything.</p> <p>Note: You can test the code directly in a console app.</p> <p>JUST FOR INFORMATION... following supercat answer, I fixed my code with proposed solution and it works nicely :-) !!! The bad thing is the usage of a static dict and the performance the goes with it but it works. ... After few minutes, I realized that SuperCat give me all hints to do it better, to workaround the static dictionary and I did it. Code samples are:</p> <ol> <li>Code with the bug</li> <li>Code corrected but with a static ConditionalWeakTable</li> <li>Code with ConditioalWeakTable that include the SuperCat tricks (thanks so much to him !)</li> </ol> <p>Samples...</p> <pre><code>using System; using System.Collections.Generic; using System.Diagnostics; namespace WeakrefBug { // ********************************************************************** class B : IDisposable { public static List&lt;B&gt; AllBs = new List&lt;B&gt;(); public B() { AllBs.Add(this); } private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { AllBs.Remove(this); disposed = true; } } ~B() { Dispose(false); } } // ********************************************************************** class A { WeakReference _weakB = new WeakReference(new B()); ~A() { B b = _weakB.Target as B; if (b == null) { if (B.AllBs.Count == 1) { Debugger.Break(); // b Is still referenced but my weak reference can't find it, why ? } } else { b.Dispose(); } } } // ********************************************************************** class Program { static void Main(string[] args) { A a = new A(); a = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } // ********************************************************************** } </code></pre> <p>Version corrected:</p> <pre><code>using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; namespace WeakrefBug // Working fine with ConditionalWeakTable { // ********************************************************************** class B : IDisposable { public static List&lt;B&gt; AllBs = new List&lt;B&gt;(); public B() { AllBs.Add(this); } private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { AllBs.Remove(this); disposed = true; } } ~B() { Dispose(false); } } // ********************************************************************** class A { private static readonly System.Runtime.CompilerServices.ConditionalWeakTable&lt;A, B&gt; WeakBs = new ConditionalWeakTable&lt;A, B&gt;(); public A() { WeakBs.Add(this, new B()); } public B CreateNewB() { B b = new B(); WeakBs.Remove(this); WeakBs.Add(this, b); return b; } ~A() { B b; WeakBs.TryGetValue(this, out b); if (b == null) { if (B.AllBs.Count == 1) { Debugger.Break(); // B Is still referenced but my weak reference can't find it, why ? } } else { b.Dispose(); } } } // ********************************************************************** class Program { static void Main(string[] args) { A a = new A(); WeakReference weakB = new WeakReference(a.CreateNewB()); // Usually don't need the internal value, but only to ensure proper functionnality a = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Debug.Assert(!weakB.IsAlive); } } // ********************************************************************** } </code></pre> <p>Code with ConditioalWeakTable that include the SuperCat tricks (thanks so much to him !)</p> <pre><code>using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; namespace WeakrefBug // Working fine with non static ConditionalWeakTable - auto cleanup { // ********************************************************************** class B : IDisposable { public static List&lt;B&gt; AllBs = new List&lt;B&gt;(); public B() { AllBs.Add(this); } private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { AllBs.Remove(this); disposed = true; } } ~B() { Dispose(false); } } // ********************************************************************** class A { private ConditionalWeakTable&lt;object, object&gt; _weakBs = null; public A() { } public B CreateNewB() { B b = new B(); if (_weakBs == null) { _weakBs = new ConditionalWeakTable&lt;object, object&gt;(); _weakBs.Add(b, _weakBs); } _weakBs.Remove(this); _weakBs.Add(this, b); return b; } internal ConditionalWeakTable&lt;object, object&gt; ConditionalWeakTable // TestOnly { get { return _weakBs; } } ~A() { object objB; _weakBs.TryGetValue(this, out objB); if (objB == null) { if (B.AllBs.Count == 1) { Debugger.Break(); // B Is still referenced but my weak reference can't find it, why ? } } else { ((B)objB).Dispose(); } } } // ********************************************************************** class Program { static void Main(string[] args) { A a = new A(); WeakReference weakB = new WeakReference(a.CreateNewB()); // Usually don't need the internal value, but only to ensure proper functionnality WeakReference weakConditionalWeakTable = new WeakReference(a.ConditionalWeakTable); a = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Debug.Assert(!weakB.IsAlive); Debug.Assert(!weakConditionalWeakTable.IsAlive); } } // ********************************************************************** } </code></pre> <p>Following question of CitizenInsane... I don't remember exactly why I did what I did... I found my sample but wasn't sure about my intention at that time. I tried to figure it out and came with the following code which I thing is more clear but still don't remember my original need. Sorry ???</p> <pre><code>using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; namespace WeakrefBug // Working fine with ConditionalWeakTable { // ********************************************************************** class B : IDisposable { public static List&lt;B&gt; AllBs = new List&lt;B&gt;(); public B() { AllBs.Add(this); } private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { AllBs.Remove(this); disposed = true; } } ~B() { Dispose(false); } } // ********************************************************************** class A { private ConditionalWeakTable&lt;object, object&gt; _weakBs = null; private WeakReference _weakB = null; public A() { _weakBs = new ConditionalWeakTable&lt;object, object&gt;(); B b = new B(); _weakB = new WeakReference(b); _weakBs.Add(b, _weakB); } public B B { get { return _weakB.Target as B; } set { _weakB.Target = value; } } internal ConditionalWeakTable&lt;object, object&gt; ConditionalWeakTable // TestOnly { get { return _weakBs; } } ~A() { B objB = B; if (objB == null) { if (B.AllBs.Count == 1) { Debugger.Break(); // B Is still referenced but my weak reference can't find it, why ? } } else { ((B)objB).Dispose(); } } } // ********************************************************************** class Program { static void Main(string[] args) { Test1(); Test2(); } private static void Test1() { A a = new A(); WeakReference weakB = new WeakReference(a.B); // Usually don't need the internal value, but only to ensure proper functionnality WeakReference weakConditionalWeakTable = new WeakReference(a.ConditionalWeakTable); a = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Debug.Assert(B.AllBs.Count == 0); GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Debug.Assert(!weakB.IsAlive); // Need second pass of Collection to be collected Debug.Assert(!weakConditionalWeakTable.IsAlive); } private static void Test2() { A a = new A(); WeakReference weakB = new WeakReference(a.B); B.AllBs.Clear(); a.B = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Debug.Assert(!weakB.IsAlive); // Need second pass of Collection to be collected } } // ********************************************************************** } </code></pre>
 

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