Collections Data Structure C#

/*
/// 
///   Copied with minor mod from CAB objectBuilder; added CleanIfNeeded operation
/// 

*/
using System;
using System.Collections.Generic;
using System.Linq;
namespace IdeaBlade.Application.Framework.Core.Collections.Generic
{
    /// 
    /// Represents a dictionary which stores the values as weak references instead of strong
    /// references. Null values are supported.
    /// 

    /// 
    /// 
    public class WeakRefDictionary
    {
        /// 
        /// Initializes a new instance of the  class.
        /// 

        public WeakRefDictionary()
        {
        }
        /// 
        /// Retrieves a value from the dictionary.
        /// 

        /// The key to look for.
        /// The value in the dictionary.
        /// 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.
        public TValue this[TKey key]
        {
            get
            {
                CleanIfNeeded();
                TValue result;
                if (TryGetValue(key, out result)) return result;
                throw new KeyNotFoundException();
            }
        }
        /// 
        /// Returns a count of the number of items in the dictionary.
        /// 

        /// 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.

        public int Count
        {
            get
            {
                CleanAbandonedItems();
                return _inner.Count;
            }
        }
        /// 
        /// Adds a new item to the dictionary.
        /// 

        /// The key.
        /// The value.
        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)));
        }
        /// 
        /// Determines if the dictionary contains a value for the key.
        /// 

        /// 
        /// 
        public bool ContainsKey(TKey key)
        {
            TValue dummy;
            return TryGetValue(key, out dummy);
        }
        /// 
        /// Gets an enumerator over the values in the dictionary.
        /// 

        /// The enumerator.
        /// 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.

        public IEnumerator> GetEnumerator()
        {
            foreach (KeyValuePair kvp in _inner)
            {
                object innerValue = kvp.Value.Target;
                if (innerValue != null)
                {
                    yield return new KeyValuePair(kvp.Key, DecodeNullObject(innerValue));
                }
            }
        }
        /// 
        /// Removes an item from the dictionary.
        /// 

        /// The key of the item to be removed.
        /// Returns true if the key was in the dictionary; return false otherwise.
        public bool Remove(TKey key)
        {
            return _inner.Remove(key);
        }
        /// 
        /// Attempts to get a value from the dictionary.
        /// 

        /// The key
        /// The value
        /// Returns true if the value was present; false otherwise.
        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(result);
            return true;
        }
        /// Removes all keys and values from the Dictionary.
        public void Clear()
        {
            _inner.Clear();
        }
        private object EncodeNullObject(object value)
        {
            if (value == null)
            {
                return typeof(NullObject);
            }
            else
            {
                return value;
            }
        }
        private TObject DecodeNullObject(object innerValue)
        {
            if (innerValue is Type && (Type)innerValue == typeof(NullObject))
            {
                return default(TObject);
            }
            else
            {
                return (TObject)innerValue;
            }
        }
        private class NullObject
        {
        }
        /// 
        /// Perform cleanup if GC occurred
        /// 

        private void CleanIfNeeded()
        {
            if (_gcSentinal.Target == null)
            {
                CleanAbandonedItems();
                _gcSentinal = new WeakReference(new Object());
            }
        }
        private void CleanAbandonedItems()
        {
            //List deadKeys = new List();
            //foreach (KeyValuePair 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 _inner = new Dictionary();
        /// 
        /// 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
        /// 

        private WeakReference _gcSentinal = new WeakReference(new Object());
    }
}