IDictionary
内部で弱参照を使用する適切な実装はどこにありますか?
ディクショナリは値への弱参照のみを保持し、最終的にはデッド参照をクリーンアップする必要があります。
それとも私はそれを自分で書くべきですか?
IDictionary
内部で弱参照を使用する適切な実装はどこにありますか?
ディクショナリは値への弱参照のみを保持し、最終的にはデッド参照をクリーンアップする必要があります。
それとも私はそれを自分で書くべきですか?
ConditionalWeakTable クラスは弱いキーを使用し、キーへの他の参照がテーブルの外に存在しなくなるとすぐに、キー/値エントリを自動的に削除します。
自分で書く必要があります。IDictionary<T,T>
インターフェイスを実装してから、実際の値をWeakReferences<T>
. その後、 add/select を使用して値をチェックし、TryGetTarget
それらがまだ生きているかどうかを確認できます。
public class WeakDictionary <TKey,TValue> : IDictionary<TKey,TValue>
where TValue : class
{
private readonly Dictionary<TKey,WeakReference<TValue>> innerDictionary = new Dictionary<TKey,WeakReference<TValue>>();
public TValue Index[ TKey key ]
{
get
{
// Use .TryGetTarget instead of .IsAlive and .Target
if (this.innerDictionary.TryGetValue(key, out WeakReference<TValue> wf) && wf.TryGetTarget(out TValue value))
{
return value;
}
return null;
}
}
private void Cull()
{
var deadKeys = this.innerDictionary.Where(kvp => kvp.Value.IsAlive).Select(kvp => kvp.Key).ToList();
foreach (var key in deadKeys)
{
_ = this.innerDictionary.TryRemove(key);
}
}
}
WeakReferenceオブジェクトのディクショナリを保持するだけの問題の1つは、ディクショナリ全体を列挙する以外に、ターゲットがスコープ外になるWeakReferenceオブジェクトをディクショナリから削除する方法がないことです。
プライマリターゲットがスコープ外になったときに呼び出されるデリゲートをWeakReferenceに含めることができれば便利です。私の知る限り、それを行う方法はありません。「弱い辞書」内に格納しているオブジェクトに別のフィールドと小さなコードを追加してもかまわない場合は、「Finasposer」オブジェクトと呼ばれるものを作成することをお勧めします。このオブジェクトの唯一のフィールドはMethodInvokerです。廃棄する場合、MethodInvokerは無効にする必要があります。ファイナライザーは、MethodInvokerをInterlocked.Exchange()してnullにし、(古い値がnullでない場合は)それを呼び出す必要があります。ディクショナリに書き込まれるオブジェクトは、新しいFinasposerオブジェクトを作成する必要があります。デリゲートを使用すると、都合のよいときにキーがディクショナリから削除されます。
ファイナライザーもそれによって呼び出されるデリゲートも、辞書を直接操作したり、ロックの取得を必要とするようなことをしたりしてはならないことに注意してください。Finasposerがデリゲートを保持している場合、Finalizeの実行時にそのデリゲート自体が有効であることが保証されますが、デリゲートにアタッチされているオブジェクト、およびそれによって参照されるオブジェクトは、予期しない状態になる可能性があります。ただし、Finasposerで呼び出されたメソッドが、スコープ外になったオブジェクトへの参照をリンクリストに追加することは安全です。辞書のAdd、Remove、およびその他のメソッドは、リンクリストをポーリングして、その中のWeakReferencesのいずれかが死んでいて、クリーンアップする必要があるかどうかを確認できます。
値へのWeakReferencesがあることは1つのことですが、ディクショナリキーもメモリリークの原因になる可能性があることがわかりました。キーへのWeakReferenceを使用した必要最低限の実装は次のとおりです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Common.library.collections {
/// <summary>
/// THIS DICTIONARY WILL NOT "HANG ON" TO THE KEYS IT USES
/// IF THE KEY IS GARBAGE COLLECTED, THE VALUE WILL BE RELEASED TOO
/// </summary>
public class Dictionary_usingWeakKey<K, V> {
//MAP FROM HASH CODE TO LIST OF KEY/VALUE PAIRS
private Dictionary<int, List<Pair>> dic = new Dictionary<int, List<Pair>>();
public void Add(K key, V value) {
if (value==null){
this.Remove(key);
return;
}//endif
List<Pair> list = null;
dic.TryGetValue(key.GetHashCode(), out list);
if (list == null) {
list = new List<Pair>();
dic.Add(key.GetHashCode(), list);
}//endif
Boolean isDirty = false;
foreach(Pair p in list){
if (p.Key.Target == null) {
isDirty = true;
continue;
}//endif
if (p.Key.Target == (Object)key) {
p.Value = (Object)value;
if (isDirty) cleanList(list);
return;
}//endif
}//for
if (isDirty) cleanList(list);
Pair newP=new Pair();
newP.Key = new WeakReference(key);
newP.Value = value;
list.Add(newP);
}//method
public bool ContainsKey(K key) {
List<Pair> list = null;
dic.TryGetValue(key.GetHashCode(), out list);
if (list == null) return false;
Boolean isDirty = false;
foreach (Pair p in list) {
if (p.Key.Target == null) {
isDirty = true;
continue;
}//endif
if (p.Key.Target == (Object)key) {
if (isDirty) cleanList(list);
return true;
}//endif
}//for
if (isDirty) cleanList(list);
return false;
}//method
private void cleanList(List<Pair> list) {
var temp = (from Pair p in list where p.Key.Target != null select p);
list.Clear();
list.AddRange(temp);
}//method
public bool Remove(K key) {
List<Pair> list = null;
dic.TryGetValue(key.GetHashCode(), out list);
if (list == null) return true;
foreach (Pair p in list) {
if (p.Key.Target == (Object)key) {
p.Value = null;
break;
}//endif
}//for
cleanList(list);
return true;
}//method
public V this[K key] {
get {
List<Pair> list = null;
dic.TryGetValue(key.GetHashCode(), out list);
if (list == null) return default(V);
Boolean isDirty = false;
foreach (Pair p in list) {
if (p.Key.Target == null) {
isDirty = true;
continue;
}//endif
if (p.Key.Target == (Object)key) {
if (isDirty) cleanList(list);
return (V)p.Value;
}//endif
}//for
if (isDirty) cleanList(list);
return default(V);
}
set {
this.Add(key, value);
}
}
public void Add(KeyValuePair<K, V> item) {
throw new NotImplementedException();
}
public void Clear() {
dic.Clear();
}
public bool Contains(KeyValuePair<K, V> item) {
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) {
throw new NotImplementedException();
}
public int Count {
get {
throw new NotImplementedException();
//return dic.Count();
}
}
public bool IsReadOnly {
get { return false; }
}
public bool Remove(KeyValuePair<K, V> item) {
throw new NotImplementedException();
}
public IEnumerator<KeyValuePair<K, V>> GetEnumerator() {
throw new NotImplementedException();
//return dic.GetEnumerator();
}
//System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
// return ((System.Collections.IEnumerable)dic).GetEnumerator();
//}
}//class
public class Pair{
public WeakReference Key;
public Object Value;
}//method
}