/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// using System; using System.Threading; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; namespace wasSharp.Collections.Specialized { /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// /// /// An implementation of an observable HashSet. /// /// the object type public class ObservableConcurrentHashSet : ICollection, INotifyCollectionChanged, IEnumerable { private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); private readonly HashSet store = new HashSet(); public ObservableConcurrentHashSet(HashSet set) { _lock.EnterWriteLock(); try { UnionWith(set); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } public ObservableConcurrentHashSet() { } public ObservableConcurrentHashSet(T item) { _lock.EnterWriteLock(); try { Add(item); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } public ObservableConcurrentHashSet(ObservableConcurrentHashSet other) { _lock.EnterWriteLock(); try { UnionWith(other); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } public ObservableConcurrentHashSet(IEnumerable list) { _lock.EnterWriteLock(); try { UnionWith(list); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } public bool IsVirgin { get; private set; } = true; public IEnumerator GetEnumerator() { _lock.EnterReadLock(); try { using (var enumerator = store.GetEnumerator()) { while (enumerator.MoveNext()) yield return enumerator.Current; } } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } } IEnumerator IEnumerable.GetEnumerator() { _lock.EnterReadLock(); try { using (var enumerator = store.GetEnumerator()) { while (enumerator.MoveNext()) yield return enumerator.Current; } } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } } public void Add(T item) { _lock.EnterWriteLock(); try { store.Add(item); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } IsVirgin = false; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } public void Clear() { _lock.EnterWriteLock(); try { store.Clear(); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } if (!IsVirgin) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); IsVirgin = false; } public bool Contains(T item) { _lock.EnterReadLock(); try { return store.Contains(item); } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } } public void CopyTo(T[] array, int arrayIndex) { _lock.EnterReadLock(); try { store.CopyTo(array, arrayIndex); } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } } public bool Remove(T item) { _lock.EnterWriteLock(); bool removed; try { removed = store.Remove(item); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } IsVirgin = false; if (removed) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item)); return removed; } public int Count => store.Count(); public bool IsReadOnly => false; public event NotifyCollectionChangedEventHandler CollectionChanged; public void UnionWith(IEnumerable list) { var added = new List(list.Except(store)); _lock.EnterWriteLock(); try { store.UnionWith(added); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } if (!IsVirgin && added.Any()) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added)); IsVirgin = false; } private void OnCollectionChanged(NotifyCollectionChangedEventArgs args) { CollectionChanged?.Invoke(this, args); } public void ExceptWith(IEnumerable list) { var removed = new List(list.Intersect(store)); _lock.EnterWriteLock(); try { store.ExceptWith(removed); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } if (!IsVirgin && removed.Any()) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed)); IsVirgin = false; } public void RemoveWhere(Func func) { var removed = new List(store.Where(func)); _lock.EnterWriteLock(); try { store.ExceptWith(removed); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } if (!IsVirgin && removed.Any()) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed)); IsVirgin = false; } public IEnumerable AsEnumerable() { _lock.EnterWriteLock(); try { return store.AsEnumerable(); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } } }