Mega Code Archive

 
Categories / C# / Collections Data Structure
 

Represents a dictionary which stores the values as weak references instead of strong references

/* /// <changelog> ///   <item who="jtraband" when="May-05-2009">Copied with minor mod from CAB objectBuilder; added CleanIfNeeded operation</item> /// </changelog> */ using System; using System.Collections.Generic; using System.Linq; namespace IdeaBlade.Application.Framework.Core.Collections.Generic {     /// <summary>     /// Represents a dictionary which stores the values as weak references instead of strong     /// references. Null values are supported.     /// </summary>     /// <typeparam name="TKey"></typeparam>     /// <typeparam name="TValue"></typeparam>     public class WeakRefDictionary<TKey, TValue>     {         /// <summary>         /// Initializes a new instance of the <see cref="WeakRefDictionary{K,V}"/> class.         /// </summary>         public WeakRefDictionary()         {         }         /// <summary>         /// Retrieves a value from the dictionary.         /// </summary>         /// <param name="key">The key to look for.</param>         /// <returns>The value in the dictionary.</returns>         /// <exception cref="KeyNotFoundException">Thrown when the key does exist in the dictionary.         /// Since the dictionary contains weak references, the key may have been removed by the         /// garbage collection of the object.</exception>         public TValue this[TKey key]         {             get             {                 CleanIfNeeded();                 TValue result;                 if (TryGetValue(key, out result)) return result;                 throw new KeyNotFoundException();             }         }         /// <summary>         /// Returns a count of the number of items in the dictionary.         /// </summary>         /// <remarks>Since the items in the dictionary are held by weak reference, the count value         /// cannot be relied upon to guarantee the number of objects that would be discovered via         /// enumeration. Treat the Count as an estimate only.  This property also has the side effect          /// of clearing out any GC'd refs.</remarks>         public int Count         {             get             {                 CleanAbandonedItems();                 return _inner.Count;             }         }         /// <summary>         /// Adds a new item to the dictionary.         /// </summary>         /// <param name="key">The key.</param>         /// <param name="value">The value.</param>         public void Add(TKey key, TValue value)         {             TValue dummy;             CleanIfNeeded();             if (TryGetValue(key, out dummy))             {                 throw new ArgumentException("KeyIsAlreadyPresentInThisDictionary");             }             _inner.Add(key, new WeakReference(EncodeNullObject(value)));         }         /// <summary>         /// Determines if the dictionary contains a value for the key.         /// </summary>         /// <param name="key"></param>         /// <returns></returns>         public bool ContainsKey(TKey key)         {             TValue dummy;             return TryGetValue(key, out dummy);         }         /// <summary>         /// Gets an enumerator over the values in the dictionary.         /// </summary>         /// <returns>The enumerator.</returns>         /// <remarks>As objects are discovered and returned from the enumerator, a strong reference         /// is temporarily held on the object so that it will continue to exist for the duration of         /// the enumeration. Once the enumeration of that object is over, the strong reference is         /// removed. If you wish to keep values alive for use after enumeration, to ensure that they         /// stay alive, you should store strong references to them during enumeration.</remarks>         public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()         {             foreach (KeyValuePair<TKey, WeakReference> kvp in _inner)             {                 object innerValue = kvp.Value.Target;                 if (innerValue != null)                 {                     yield return new KeyValuePair<TKey, TValue>(kvp.Key, DecodeNullObject<TValue>(innerValue));                 }             }         }         /// <summary>         /// Removes an item from the dictionary.         /// </summary>         /// <param name="key">The key of the item to be removed.</param>         /// <returns>Returns true if the key was in the dictionary; return false otherwise.</returns>         public bool Remove(TKey key)         {             return _inner.Remove(key);         }         /// <summary>         /// Attempts to get a value from the dictionary.         /// </summary>         /// <param name="key">The key</param>         /// <param name="value">The value</param>         /// <returns>Returns true if the value was present; false otherwise.</returns>         public bool TryGetValue(TKey key, out TValue value)         {             value = default(TValue);             WeakReference wr;             if (!_inner.TryGetValue(key, out wr)) return false;             object result = wr.Target;             if (result == null)             {                 _inner.Remove(key);                 return false;             }             value = DecodeNullObject<TValue>(result);             return true;         }         /// <summary>Removes all keys and values from the Dictionary.</summary>         public void Clear()         {             _inner.Clear();         }         private object EncodeNullObject(object value)         {             if (value == null)             {                 return typeof(NullObject);             }             else             {                 return value;             }         }         private TObject DecodeNullObject<TObject>(object innerValue)         {             if (innerValue is Type && (Type)innerValue == typeof(NullObject))             {                 return default(TObject);             }             else             {                 return (TObject)innerValue;             }         }         private class NullObject         {         }         /// <summary>         /// Perform cleanup if GC occurred         /// </summary>         private void CleanIfNeeded()         {             if (_gcSentinal.Target == null)             {                 CleanAbandonedItems();                 _gcSentinal = new WeakReference(new Object());             }         }         private void CleanAbandonedItems()         {             //List<TKey> deadKeys = new List<TKey>();             //foreach (KeyValuePair<TKey, WeakReference> kvp in _inner) {             //  if (kvp.Value.Target == null) deadKeys.Add(kvp.Key);             //}             // foreach (TKey key in deadKeys) _inner.Remove(key);             _inner.Where(kvp => kvp.Value.Target == null)              .Select(kvp => kvp.Key)              .ToList()              .ForEach(k => _inner.Remove(k));         }         private Dictionary<TKey, WeakReference> _inner = new Dictionary<TKey, WeakReference>();         /// <summary>         /// Serves as a simple "GC Monitor" that indicates whether cleanup is needed.          /// If _gcSentinal.IsAlive is false, GC has occurred and we should perform cleanup         /// </summary>         private WeakReference _gcSentinal = new WeakReference(new Object());     } }