// // // // // $Revision$ // using System; using System.Collections.Generic; namespace ICSharpCode.TextEditor.Util { /// /// A collection that does not allows its elements to be garbage-collected (unless there are other /// references to the elements). Elements will disappear from the collection when they are /// garbage-collected. /// /// The WeakCollection is not thread-safe, not even for read-only access! /// No methods may be called on the WeakCollection while it is enumerated, not even a Contains or /// creating a second enumerator. /// The WeakCollection does not preserve any order among its contents; the ordering may be different each /// time the collection is enumerated. /// /// Since items may disappear at any time when they are garbage collected, this class /// cannot provide a useful implementation for Count and thus cannot implement the ICollection interface. /// public class WeakCollection : IEnumerable where T : class { readonly List innerList = new List(); /// /// Adds an element to the collection. Runtime: O(n). /// public void Add(T item) { if (item == null) throw new ArgumentNullException("item"); CheckNoEnumerator(); if (innerList.Count == innerList.Capacity || (innerList.Count % 32) == 31) innerList.RemoveAll(delegate(WeakReference r) { return !r.IsAlive; }); innerList.Add(new WeakReference(item)); } /// /// Removes all elements from the collection. Runtime: O(n). /// public void Clear() { innerList.Clear(); CheckNoEnumerator(); } /// /// Checks if the collection contains an item. Runtime: O(n). /// public bool Contains(T item) { if (item == null) throw new ArgumentNullException("item"); CheckNoEnumerator(); foreach (T element in this) { if (item.Equals(element)) return true; } return false; } /// /// Removes an element from the collection. Returns true if the item is found and removed, /// false when the item is not found. /// Runtime: O(n). /// public bool Remove(T item) { if (item == null) throw new ArgumentNullException("item"); CheckNoEnumerator(); for (int i = 0; i < innerList.Count;) { T element = (T)innerList[i].Target; if (element == null) { RemoveAt(i); } else if (element == item) { RemoveAt(i); return true; } else { i++; } } return false; } void RemoveAt(int i) { int lastIndex = innerList.Count - 1; innerList[i] = innerList[lastIndex]; innerList.RemoveAt(lastIndex); } bool hasEnumerator; void CheckNoEnumerator() { if (hasEnumerator) throw new InvalidOperationException("The WeakCollection is already being enumerated, it cannot be modified at the same time. Ensure you dispose the first enumerator before modifying the WeakCollection."); } /// /// Enumerates the collection. /// Each MoveNext() call on the enumerator is O(1), thus the enumeration is O(n). /// public IEnumerator GetEnumerator() { if (hasEnumerator) throw new InvalidOperationException("The WeakCollection is already being enumerated, it cannot be enumerated twice at the same time. Ensure you dispose the first enumerator before using another enumerator."); try { hasEnumerator = true; for (int i = 0; i < innerList.Count;) { T element = (T)innerList[i].Target; if (element == null) { RemoveAt(i); } else { yield return element; i++; } } } finally { hasEnumerator = false; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } }