|
|
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
-
- namespace System.Runtime.CompilerServices
- {
- public sealed class ConditionalWeakTable<TKey, TValue> where TKey : class where TValue : class
- {
- private readonly Dictionary<WeakReference<TKey>, TValue> items = new Dictionary<WeakReference<TKey>, TValue>();
- private readonly object _lock = new object();
-
- private sealed class KeyComparer : IEqualityComparer<WeakReference<TKey>>
- {
- public bool Equals(WeakReference<TKey> x, WeakReference<TKey> y)
- => x.TryGetTarget(out var keyX) && y.TryGetTarget(out var keyY) && ReferenceEquals(keyX, keyY);
-
- public int GetHashCode(WeakReference<TKey> obj)
- => obj.TryGetTarget(out var key) ? key.GetHashCode() : 0;
- }
-
- private static WeakReference<TKey> WeakRef(TKey key)
- => new WeakReference<TKey>(key);
-
- private sealed class GCTracker
- {
- public static event Action OnGC;
- private static readonly WeakReference<GCTracker> tracker = new WeakReference<GCTracker>(new GCTracker());
- ~GCTracker()
- {
- OnGC?.Invoke();
- if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)
- tracker.SetTarget(new GCTracker());
- }
- }
-
- public void Add(TKey key, TValue value)
- {
- lock (_lock)
- items.Add(WeakRef(key), value);
- }
-
- public bool TryGetValue(TKey key, out TValue value)
- {
- if (key == null)
- throw new ArgumentException("Null key", nameof(key));
-
- value = null;
- lock (_lock)
- return items.TryGetValue(WeakRef(key), out value);
- }
-
- public delegate TValue CreateValueCallback(TKey key);
-
- public TValue GetValue(TKey key, CreateValueCallback createValueCallback)
- {
- if (createValueCallback == null)
- throw new ArgumentException("Null create delegate", nameof(createValueCallback));
-
- lock (_lock)
- {
- if (TryGetValue(key, out var value))
- return value;
- else
- {
- value = createValueCallback(key);
- Add(key, value);
- return value;
- }
- }
- }
-
- public TValue GetOrCreateValue(TKey key)
- => GetValue(key, k => Activator.CreateInstance<TValue>());
-
- public bool Remove(TKey key)
- => items.Remove(WeakRef(key));
-
- public ConditionalWeakTable()
- => GCTracker.OnGC += OnGC;
- ~ConditionalWeakTable()
- => GCTracker.OnGC -= OnGC;
-
- private void OnGC()
- {
- // on each GC, we want to clear the entire set of empty keys
- var nullWeakRef = WeakRef(null);
- while (items.Remove(nullWeakRef)) ; // just loop
- }
- }
- }
|