こんにちは私はここにブレークポイントに到達した理由がわからないコードがあります(コメントを参照)。
これは私が知らない、または正しく理解していない何かのマイクロソフトのバグですか?
コードはデバッグでテストされましたが、何も変更されるべきではないと思います。
注:コンソールアプリでコードを直接テストできます。
情報のためだけに...supercatの回答に続いて、提案されたソリューションでコードを修正しましたが、うまく機能します:-) !!! 悪いことは、静的dictの使用とそれに伴うパフォーマンスですが、機能します。...数分後、SuperCatが、静的辞書を回避するために、それをより良くするためのすべてのヒントを与えてくれることに気づき、それを実行しました。コードサンプルは次のとおりです。
- バグのあるコード
- コードは修正されましたが、静的なConditionalWeakTableが使用されています
- SuperCatのトリックを含むConditioalWeakTableを使用したコード(彼に感謝します!)
サンプル...
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();
}
}
// **********************************************************************
}
修正されたバージョン:
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);
}
}
// **********************************************************************
}
SuperCatのトリックを含むConditioalWeakTableを使用したコード(彼に感謝します!)
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);
}
}
// **********************************************************************
}
CitizenInsaneの質問に続いて...なぜ私がやったことをしたのか正確には覚えていません...私は自分のサンプルを見つけましたが、その時の私の意図についてはわかりませんでした。私はそれを理解しようとしましたが、次のコードが付属していました。これはより明確ですが、それでも元の必要性を覚えていません。ごめん ???
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
}
}
// **********************************************************************
}