Hi I have code here where I don't understand why I hit the breakpoint (see comment).
Is this a Microsoft bug of something I don't know or I don't understand properly ?
The code was tested in Debug but I think it should not changes anything.
Note: You can test the code directly in a console app.
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:
- Code with the bug
- Code corrected but with a static ConditionalWeakTable
- Code with ConditioalWeakTable that include the SuperCat tricks (thanks so much to him !)
Samples...
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace WeakrefBug
{
// **********************************************************************
class B : IDisposable
{
public static List<B> AllBs = new List<B>();
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();
}
}
// **********************************************************************
}
Version corrected:
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<B> AllBs = new List<B>();
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<A, B> WeakBs = new ConditionalWeakTable<A, B>();
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 with ConditioalWeakTable that include the SuperCat tricks (thanks so much to him !)
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<B> AllBs = new List<B>();
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<object, object> _weakBs = null;
public A()
{
}
public B CreateNewB()
{
B b = new B();
if (_weakBs == null)
{
_weakBs = new ConditionalWeakTable<object, object>();
_weakBs.Add(b, _weakBs);
}
_weakBs.Remove(this);
_weakBs.Add(this, b);
return b;
}
internal ConditionalWeakTable<object, object> 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);
}
}
// **********************************************************************
}
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 ???
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<B> AllBs = new List<B>();
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<object, object> _weakBs = null;
private WeakReference _weakB = null;
public A()
{
_weakBs = new ConditionalWeakTable<object, object>();
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<object, object> 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
}
}
// **********************************************************************
}