/*
///
/// - 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());
}
}