複数のキーを利用できるConditionalWeakTableクラス
下記記事にて、2つのキーを利用できるWeakTableを実装してみました。
2つのキーを利用できるConditionalWeakTableクラス - 何でもプログラミング
今回は3,4個と拡張しやすいように、複数個のキーを利用できるWeakTableを実装してみたいと思います。
Keysクラス
前回は2つのWeakReferenceプロパティをもっていましたが、今回はWeakReference配列を扱うよう変更しました。
EqualsとGetHashCodeを、WeakReferenceではなくTargetで行うところは変わっていません。
class Keys { public WeakReference[] WeakKeys { get; } int _hash; public bool IsAlive() { return WeakKeys.All(x => x.IsAlive); } public Keys(object[] keys) { WeakKeys = keys.Select(x => new WeakReference(x)).ToArray(); _hash = keys.Aggregate(365011897, (hash, key) => hash * -1521134295 + key.GetHashCode()); } public override bool Equals(object obj) { var keys = obj as Keys; return keys != null && WeakKeys.Length == keys.WeakKeys.Length && WeakKeys.Zip(keys.WeakKeys, TargetEquals).All(x => x); } bool TargetEquals(WeakReference weakRef1, WeakReference weakRef2) { var ref1 = weakRef1.Target; var ref2 = weakRef2.Target; return ref1 != null && ref2 != null && Equals(ref1, ref2); } public override int GetHashCode() { return _hash; } }
WeakTableクラス
まず、一般的はkey配列に対応したクラスを作成します。
class WeakTable<TValue> { object _lockObj = new object(); Dictionary<Keys, TValue> _dictionary = new Dictionary<Keys, TValue>(); public WeakTable() { WeakEventManager<GCNotifier, EventArgs>.AddHandler(null, nameof(GCNotifier.Collected), (s, e) => CheckReferences()); } public void Add(object[] keys, TValue value) { lock (_lockObj) { _dictionary[new Keys(keys)] = value; } } public bool TryGetValue(object[] keys, ref TValue value) { lock (_lockObj) { var key = new Keys(keys); if (_dictionary.ContainsKey(key) == false) return false; value = _dictionary[key]; return true; } } public void CheckReferences() { lock (_lockObj) { _dictionary = _dictionary .Where(x => x.Key.IsAlive()) .ToDictionary(x => x.Key, x => x.Value); } } }
これを利用して、例えば2つのキーのWeakTableは下記のように定義します。
class WeakTable<TKey1, TKey2, TValue> where TKey1 : class where TKey2 : class { WeakTable<TValue> _table = new WeakTable<TValue>(); public void Add(TKey1 key1, TKey2 key2, TValue value) => _table.Add(new object[] { key1, key2 }, value); public bool TryGetValue(TKey1 key1, TKey2 key2, ref TValue value) => _table.TryGetValue(new object[] { key1, key2 }, ref value); }