/Collections/Specialized/ConcurrentMultiKeyDictionary.cs |
@@ -0,0 +1,636 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
// Based on the work of Herman Schoenfeld |
|
using System.Collections.Generic; |
using System.Linq; |
using System.Threading; |
|
namespace wasSharp.Collections.Specialized |
{ |
public class ConcurrentMultiKeyDictionary<K1, K2, V> : Dictionary<K1, Dictionary<K2, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
if (!ContainsKey(key1) || !this[key1].ContainsKey(key2)) |
{ |
return default(V); |
} |
return base[key1][key2]; |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new Dictionary<K2, V>(); |
|
this[key1][key2] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public new IEnumerable<V> Values |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return base.Values.SelectMany(baseDict => baseDict.Keys, (baseDict, baseKey) => baseDict[baseKey]); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public void Add(K1 key1, K2 key2, V value) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new Dictionary<K2, V>(); |
|
this[key1][key2] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
|
public void Remove(K1 key1, K2 key2) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1) || !this[key1].ContainsKey(key2)) |
{ |
return; |
} |
this[key1].Remove(key2); |
Remove(key1); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
public bool TryGetValue(K1 key1, K2 key2, out V value) |
{ |
_lock.EnterReadLock(); |
try |
{ |
if (!ContainsKey(key1) || !this[key1].ContainsKey(key2)) |
{ |
value = default(V); |
return false; |
} |
value = base[key1][key2]; |
return true; |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, V> : Dictionary<K1, ConcurrentMultiKeyDictionary<K2, K3, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) ? this[key1][key2, key3] : default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new ConcurrentMultiKeyDictionary<K2, K3, V>(); |
|
this[key1][key2, key3] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2, key3); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
public void Add(K1 key1, K2 key2, K3 key3, V value) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new ConcurrentMultiKeyDictionary<K2, K3, V>(); |
this[key1][key2, key3] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
|
public void Remove(K1 key1, K2 key2, K3 key3) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1) || !this[key1].ContainsKey(key2, key3)) |
{ |
return; |
} |
this[key1][key2].Remove(key3); |
this[key1].Remove(key2); |
Remove(key1); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
|
public bool TryGetValue(K1 key1, K2 key2, K3 key3, out V value) |
{ |
_lock.EnterReadLock(); |
try |
{ |
if (!ContainsKey(key1) || !this[key1].ContainsKey(key2, key3)) |
{ |
value = default(V); |
return false; |
} |
value = base[key1][key2, key3]; |
return true; |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3, K4 key4] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) ? this[key1][key2, key3, key4] : default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, V>(); |
this[key1][key2, key3, key4] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, K5, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5] : default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, V>(); |
this[key1][key2, key3, key4, key5] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, V> : |
Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6] : default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, V>(); |
this[key1][key2, key3, key4, key5, key6] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, V> : |
Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7] : default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>(); |
this[key1][key2, key3, key4, key5, key6, key7] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, V> : |
Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8] : default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>(); |
this[key1][key2, key3, key4, key5, key6, key7, key8] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, V> : |
Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9] : default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>(); |
this[key1][key2, key3, key4, key5, key6, key7, key8, key9] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, V> : |
Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) |
? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] |
: default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>(); |
this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, |
K10 key10) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && |
this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V> : |
Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
|
public V this[ |
K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10, K11 key11] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) |
? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] |
: default(V); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
if (!ContainsKey(key1)) |
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>(); |
this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, |
K10 key10, K11 key11) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ContainsKey(key1) && |
this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10, key11); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
} |
/Collections/Specialized/ObservableConcurrentHashSet.cs |
@@ -0,0 +1,272 @@ |
/////////////////////////////////////////////////////////////////////////// |
// 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 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// An implementation of an observable HashSet. |
/// </summary> |
/// <typeparam name="T">the object type</typeparam> |
public class ObservableConcurrentHashSet<T> : ICollection<T>, INotifyCollectionChanged, IEnumerable<T> |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
private readonly HashSet<T> store = new HashSet<T>(); |
|
public ObservableConcurrentHashSet(HashSet<T> 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<T> other) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
UnionWith(other); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
|
public ObservableConcurrentHashSet(IEnumerable<T> list) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
UnionWith(list); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
|
public bool IsVirgin { get; private set; } = true; |
|
public IEnumerator<T> 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<T> list) |
{ |
var added = new List<T>(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<T> list) |
{ |
var removed = new List<T>(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<T, bool> func) |
{ |
var removed = new List<T>(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<T> AsEnumerable() |
{ |
_lock.EnterWriteLock(); |
try |
{ |
return store.AsEnumerable(); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
} |
/Collections/Specialized/ObservableDictionary.cs |
@@ -1,274 +1,258 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2016 - 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.Collections; |
using System.Collections.Generic; |
using System.Collections.Specialized; |
using System.Threading; |
|
namespace wasSharp.Collections.Specialized |
{ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2016 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// An implementation of an observable Dictionary. |
/// </summary> |
/// <typeparam name="K">the key type</typeparam> |
/// <typeparam name="V">the value type</typeparam> |
public class ObservableDictionary<K, V> : IDictionary<K, V>, INotifyCollectionChanged |
{ |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); |
private readonly Dictionary<K, V> store = new Dictionary<K, V>(); |
|
public bool IsVirgin { get; private set; } = true; |
|
public V this[K key] |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return store[key]; |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
set |
{ |
_lock.EnterWriteLock(); |
try |
{ |
store[key] = value; |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
} |
} |
|
public int Count => store.Count; |
|
public bool IsReadOnly => false; |
|
public ICollection<K> Keys |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return store.Keys; |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public ICollection<V> Values |
{ |
get |
{ |
_lock.EnterReadLock(); |
try |
{ |
return store.Values; |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
} |
|
public void Add(KeyValuePair<K, V> item) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
((IDictionary<K, V>) store).Add(item); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
IsVirgin = false; |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); |
} |
|
public void Add(K key, V value) |
{ |
_lock.EnterWriteLock(); |
try |
{ |
store.Add(key, value); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
IsVirgin = false; |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, |
new KeyValuePair<K, V>(key, value))); |
} |
|
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(KeyValuePair<K, V> item) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return ((IDictionary<K, V>) store).Contains(item); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
public bool ContainsKey(K key) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return store.ContainsKey(key); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) |
{ |
_lock.EnterReadLock(); |
try |
{ |
((IDictionary<K, V>) store).CopyTo(array, arrayIndex); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
public IEnumerator<KeyValuePair<K, V>> GetEnumerator() |
{ |
_lock.EnterReadLock(); |
try |
{ |
using (var enumerator = ((IDictionary<K, V>)store).GetEnumerator()) |
{ |
while (enumerator.MoveNext()) |
yield return enumerator.Current; |
} |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
public bool Remove(KeyValuePair<K, V> item) |
{ |
_lock.EnterWriteLock(); |
bool removed; |
try |
{ |
removed = ((IDictionary<K, V>) store).Remove(item); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
IsVirgin = false; |
if (removed) |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item)); |
return removed; |
} |
|
public bool Remove(K key) |
{ |
KeyValuePair<K, V> item; |
_lock.EnterWriteLock(); |
bool removed; |
try |
{ |
if (store.ContainsKey(key)) |
item = new KeyValuePair<K, V>(key, store[key]); |
|
removed = store.Remove(key); |
} |
finally |
{ |
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); |
} |
IsVirgin = false; |
if (removed) |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item)); |
return removed; |
} |
|
public bool TryGetValue(K key, out V value) |
{ |
_lock.EnterReadLock(); |
try |
{ |
return store.TryGetValue(key, out value); |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
IEnumerator IEnumerable.GetEnumerator() |
{ |
_lock.EnterReadLock(); |
try |
{ |
using (var enumerator = ((IDictionary<K, V>)store).GetEnumerator()) |
{ |
while (enumerator.MoveNext()) |
yield return enumerator.Current; |
} |
} |
finally |
{ |
if (_lock.IsReadLockHeld) _lock.ExitReadLock(); |
} |
} |
|
public event NotifyCollectionChangedEventHandler CollectionChanged; |
|
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args) |
{ |
CollectionChanged?.Invoke(this, args); |
} |
} |
} |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
/// From https://gist.github.com/kzu/cfe3cb6e4fe3efea6d24 |
|
using System; |
using System.Collections; |
using System.Collections.Generic; |
using System.Collections.Specialized; |
using System.ComponentModel; |
using System.Diagnostics; |
using System.Dynamic; |
using System.Linq; |
using System.Text; |
using System.Threading.Tasks; |
|
namespace wasSharp.Collections.Specialized |
{ |
/// <summary> |
/// Provides a dictionary for use with data binding. |
/// </summary> |
/// <typeparam name="TKey">Specifies the type of the keys in this collection.</typeparam> |
/// <typeparam name="TValue">Specifies the type of the values in this collection.</typeparam> |
[DebuggerDisplay("Count={Count}")] |
public class ObservableDictionary<TKey, TValue> : |
ICollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>, |
INotifyCollectionChanged, INotifyPropertyChanged |
{ |
readonly IDictionary<TKey, TValue> dictionary; |
|
/// <summary>Event raised when the collection changes.</summary> |
public event NotifyCollectionChangedEventHandler CollectionChanged = (sender, args) => { }; |
|
/// <summary>Event raised when a property on the collection changes.</summary> |
public event PropertyChangedEventHandler PropertyChanged = (sender, args) => { }; |
|
/// <summary> |
/// Initializes an instance of the class. |
/// </summary> |
public ObservableDictionary() |
: this(new Dictionary<TKey, TValue>()) |
{ |
} |
|
/// <summary> |
/// Initializes an instance of the class using another dictionary as |
/// the key/value store. |
/// </summary> |
public ObservableDictionary(IDictionary<TKey, TValue> dictionary) |
{ |
this.dictionary = dictionary; |
} |
|
void AddWithNotification(KeyValuePair<TKey, TValue> item) |
{ |
AddWithNotification(item.Key, item.Value); |
} |
|
void AddWithNotification(TKey key, TValue value) |
{ |
dictionary.Add(key, value); |
|
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, |
new KeyValuePair<TKey, TValue>(key, value))); |
PropertyChanged(this, new PropertyChangedEventArgs("Count")); |
PropertyChanged(this, new PropertyChangedEventArgs("Keys")); |
PropertyChanged(this, new PropertyChangedEventArgs("Values")); |
} |
|
bool RemoveWithNotification(TKey key) |
{ |
TValue value; |
if (dictionary.TryGetValue(key, out value) && dictionary.Remove(key)) |
{ |
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, |
new KeyValuePair<TKey, TValue>(key, value))); |
PropertyChanged(this, new PropertyChangedEventArgs("Count")); |
PropertyChanged(this, new PropertyChangedEventArgs("Keys")); |
PropertyChanged(this, new PropertyChangedEventArgs("Values")); |
|
return true; |
} |
|
return false; |
} |
|
public void Clear() |
{ |
dictionary.Clear(); |
|
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); |
PropertyChanged(this, new PropertyChangedEventArgs("Count")); |
PropertyChanged(this, new PropertyChangedEventArgs("Keys")); |
PropertyChanged(this, new PropertyChangedEventArgs("Values")); |
} |
|
void UpdateWithNotification(TKey key, TValue value) |
{ |
TValue existing; |
if (dictionary.TryGetValue(key, out existing)) |
{ |
dictionary[key] = value; |
|
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, |
new KeyValuePair<TKey, TValue>(key, value), |
new KeyValuePair<TKey, TValue>(key, existing))); |
PropertyChanged(this, new PropertyChangedEventArgs("Values")); |
} |
else |
{ |
AddWithNotification(key, value); |
} |
} |
|
/// <summary> |
/// Allows derived classes to raise custom property changed events. |
/// </summary> |
protected void RaisePropertyChanged(PropertyChangedEventArgs args) |
{ |
PropertyChanged(this, args); |
} |
|
#region IDictionary<TKey,TValue> Members |
|
/// <summary> |
/// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2" />. |
/// </summary> |
/// <param name="key">The object to use as the key of the element to add.</param> |
/// <param name="value">The object to use as the value of the element to add.</param> |
public void Add(TKey key, TValue value) |
{ |
AddWithNotification(key, value); |
} |
|
/// <summary> |
/// Determines whether the <see cref="T:System.Collections.Generic.IDictionary`2" /> contains an element with the specified key. |
/// </summary> |
/// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.IDictionary`2" />.</param> |
/// <returns> |
/// true if the <see cref="T:System.Collections.Generic.IDictionary`2" /> contains an element with the key; otherwise, false. |
/// </returns> |
public bool ContainsKey(TKey key) |
{ |
return dictionary.ContainsKey(key); |
} |
|
/// <summary> |
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the <see cref="T:System.Collections.Generic.IDictionary`2" />. |
/// </summary> |
/// <returns>An <see cref="T:System.Collections.Generic.ICollection`1" /> containing the keys of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2" />.</returns> |
public ICollection<TKey> Keys |
{ |
get { return dictionary.Keys; } |
} |
|
/// <summary> |
/// Removes the element with the specified key from the <see cref="T:System.Collections.Generic.IDictionary`2" />. |
/// </summary> |
/// <param name="key">The key of the element to remove.</param> |
/// <returns> |
/// true if the element is successfully removed; otherwise, false. This method also returns false if <paramref name="key" /> was not found in the original <see cref="T:System.Collections.Generic.IDictionary`2" />. |
/// </returns> |
public bool Remove(TKey key) |
{ |
return RemoveWithNotification(key); |
} |
|
/// <summary> |
/// Gets the value associated with the specified key. |
/// </summary> |
/// <param name="key">The key whose value to get.</param> |
/// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value" /> parameter. This parameter is passed uninitialized.</param> |
/// <returns> |
/// true if the object that implements <see cref="T:System.Collections.Generic.IDictionary`2" /> contains an element with the specified key; otherwise, false. |
/// </returns> |
public bool TryGetValue(TKey key, out TValue value) |
{ |
return dictionary.TryGetValue(key, out value); |
} |
|
/// <summary> |
/// Gets an <see cref="T:System.Collections.Generic.ICollection`1" /> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2" />. |
/// </summary> |
/// <returns>An <see cref="T:System.Collections.Generic.ICollection`1" /> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2" />.</returns> |
public ICollection<TValue> Values |
{ |
get { return dictionary.Values; } |
} |
|
/// <summary> |
/// Gets or sets the element with the specified key. |
/// </summary> |
/// <param name="key">The key.</param> |
/// <returns></returns> |
public TValue this[TKey key] |
{ |
get { return dictionary[key]; } |
set { UpdateWithNotification(key, value); } |
} |
|
#endregion |
|
#region ICollection<KeyValuePair<TKey,TValue>> Members |
|
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) |
{ |
AddWithNotification(item); |
} |
|
void ICollection<KeyValuePair<TKey, TValue>>.Clear() |
{ |
dictionary.Clear(); |
|
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); |
PropertyChanged(this, new PropertyChangedEventArgs("Count")); |
PropertyChanged(this, new PropertyChangedEventArgs("Keys")); |
PropertyChanged(this, new PropertyChangedEventArgs("Values")); |
} |
|
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) => |
dictionary.Contains(item); |
|
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) |
{ |
dictionary.CopyTo(array, arrayIndex); |
} |
|
int ICollection<KeyValuePair<TKey, TValue>>.Count => |
dictionary.Count(); |
|
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => |
dictionary.IsReadOnly; |
|
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) => |
RemoveWithNotification(item.Key); |
|
#endregion |
|
#region IEnumerable<KeyValuePair<TKey,TValue>> Members |
|
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => |
dictionary.GetEnumerator(); |
|
IEnumerator IEnumerable.GetEnumerator() => |
dictionary.GetEnumerator(); |
|
#endregion |
|
#region Extensions |
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() |
{ |
return dictionary.GetEnumerator(); |
} |
#endregion |
} |
} |