#region Copyright 2011-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.Collections.Generic; using CSharpTest.Net.Interfaces; using CSharpTest.Net.Synchronization; namespace CSharpTest.Net.Collections { /// /// Represents a thread-safe generic collection of key/value pairs. /// public class SynchronizedDictionary : IDictionary, IDisposable, IDictionaryEx, IConcurrentDictionary { private IDictionary _store; private readonly ILockStrategy _lock; /// /// Constructs a thread-safe generic collection of key/value pairs using exclusive locking. /// public SynchronizedDictionary() : this(new Dictionary(), new ExclusiveLocking()) { } /// /// Constructs a thread-safe generic collection of key/value pairs using exclusive locking. /// public SynchronizedDictionary(IEqualityComparer comparer) : this(new Dictionary(comparer), new ExclusiveLocking()) { } /// /// Constructs a thread-safe generic collection of key/value pairs using the lock provided. /// public SynchronizedDictionary(IEqualityComparer comparer, ILockStrategy locking) : this(new Dictionary(comparer), locking) { } /// /// Constructs a thread-safe generic collection of key/value pairs using the lock provided. /// public SynchronizedDictionary(ILockStrategy locking) : this(new Dictionary(), locking) { } /// /// Constructs a thread-safe generic collection of key/value pairs using the default locking /// type for exclusive access, akin to placing lock(this) around each call. If you want to /// allow reader/writer locking provide one of those lock types from the Synchronization /// namespace. /// public SynchronizedDictionary(IDictionary storage) : this(storage, new ExclusiveLocking()) { } /// /// Constructs a thread-safe generic collection of key/value pairs. /// public SynchronizedDictionary(IDictionary storage, ILockStrategy locking) { _store = Check.NotNull(storage); _lock = Check.NotNull(locking); } /// /// Defines a method to release allocated resources. /// public void Dispose() { _lock.Dispose(); if (_store is IDisposable) ((IDisposable) _store).Dispose(); _store = null; } /// Exposes the interal lock so that you can syncronize several calls public ILockStrategy Lock { get { return _lock; } } /// /// Gets a value indicating whether the is read-only. /// public bool IsReadOnly { get { return _store.IsReadOnly; } } /// /// Gets the number of elements contained in the . /// public int Count { get { using (_lock.Read()) return _store.Count; } } /// /// Locks the collection and replaces the underlying storage dictionary. /// public IDictionary ReplaceStorage(IDictionary newStorage) { using (_lock.Write()) { IDictionary storage = _store; _store = Check.NotNull(newStorage); return storage; } } /// /// Gets or sets the element with the specified key. /// public TValue this[TKey key] { get { using (_lock.Read()) return _store[key]; } set { using (_lock.Write()) _store[key] = value; } } /// /// Adds an element with the provided key and value to the . /// public void Add(TKey key, TValue value) { using (_lock.Write()) _store.Add(key, value); } void ICollection>.Add(KeyValuePair item) { using (_lock.Write()) _store.Add(item); } /// /// Removes the element with the specified key from the . /// public bool Remove(TKey key) { using (_lock.Write()) return _store.Remove(key); } bool ICollection>.Remove(KeyValuePair item) { using (_lock.Write()) return _store.Remove(item); } /// /// Removes all items from the . /// public void Clear() { using (_lock.Write()) _store.Clear(); } bool ICollection>.Contains(KeyValuePair item) { using (_lock.Read()) return _store.Contains(item); } /// /// Determines whether the contains an element with the specified key. /// public bool ContainsKey(TKey key) { using (_lock.Read()) return _store.ContainsKey(key); } /// /// Gets the value associated with the specified key. /// public bool TryGetValue(TKey key, out TValue value) { using (_lock.Read()) return _store.TryGetValue(key, out value); } /// /// Gets an containing the keys of the . /// public ICollection Keys { get { using (_lock.Read()) return new List(_store.Keys); } } /// /// Gets an containing the values in the . /// public ICollection Values { get { using (_lock.Read()) return new List(_store.Values); } } /// /// Copies the elements of the to an , starting at a particular index. /// public void CopyTo(KeyValuePair[] array, int arrayIndex) { using (_lock.Read()) _store.CopyTo(array, arrayIndex); } /// /// Returns an enumerator that iterates through the collection. /// public IEnumerator> GetEnumerator() { using (_lock.Read()) { foreach (KeyValuePair kv in _store) yield return kv; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #region IConcurrentDictionary Members /// /// Adds a key/value pair to the if the key does not already exist. /// /// The key of the element to add. /// The value to be added, if the key does not already exist. /// The is read-only. public TValue GetOrAdd(TKey key, TValue value) { using (_lock.Write()) { TValue found; if (_store.TryGetValue(key, out found)) return found; _store.Add(key, value); return value; } } /// /// Adds a key/value pair to the if the key does not already exist. /// /// The key of the element to add. /// Constructs a new value for the key. /// The is read-only. public TValue GetOrAdd(TKey key, Converter fnCreate) { using (_lock.Write()) { TValue value; if (_store.TryGetValue(key, out value)) return value; _store.Add(key, value = fnCreate(key)); return value; } } /// /// Adds an element with the provided key and value to the . /// /// The object to use as the key of the element to add. /// The object to use as the value of the element to add. /// The is read-only. public bool TryAdd(TKey key, TValue value) { using (_lock.Write()) { if (_store.ContainsKey(key)) return false; _store.Add(key, value); return true; } } /// /// Adds an element with the provided key and value to the /// by calling the provided factory method to construct the value if the key is not already present in the collection. /// public bool TryAdd(TKey key, Converter fnCreate) { using (_lock.Write()) { if (_store.ContainsKey(key)) return false; TValue value = fnCreate(key); _store.Add(key, value); return true; } } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair if the key already exists. /// public TValue AddOrUpdate(TKey key, TValue addValue, KeyValueUpdate fnUpdate) { using (_lock.Write()) { TValue value; if (_store.TryGetValue(key, out value)) { value = fnUpdate(key, value); _store[key] = value; return value; } _store.Add(key, addValue); return addValue; } } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair if the key already exists. /// public TValue AddOrUpdate(TKey key, Converter fnCreate, KeyValueUpdate fnUpdate) { using (_lock.Write()) { TValue value; if (_store.TryGetValue(key, out value)) { value = fnUpdate(key, value); _store[key] = value; return value; } value = fnCreate(key); _store.Add(key, value); return value; } } /// /// Add, update, or fetche a key/value pair from the dictionary via an implementation of the /// interface. /// public bool AddOrUpdate(TKey key, ref T createOrUpdateValue) where T : ICreateOrUpdateValue { using (_lock.Write()) { TValue value; if (_store.TryGetValue(key, out value)) { if(createOrUpdateValue.UpdateValue(key, ref value)) { _store[key] = value; return true; } return false; } if (createOrUpdateValue.CreateValue(key, out value)) { _store.Add(key, value); return true; } return false; } } /// /// Updates an element with the provided key to the value if it exists. /// /// Returns true if the key provided was found and updated to the value. /// The object to use as the key of the element to update. /// The new value for the key if found. /// The is read-only. public bool TryUpdate(TKey key, TValue value) { using (_lock.Write()) { if (_store.ContainsKey(key)) { _store[key] = value; return true; } return false; } } /// /// Updates an element with the provided key to the value if it exists. /// /// Returns true if the key provided was found and updated to the value. /// The object to use as the key of the element to update. /// The new value for the key if found. /// The value that is compared to the value of the element with key. /// The is read-only. public bool TryUpdate(TKey key, TValue value, TValue comparisonValue) { using (_lock.Write()) { TValue test; if (_store.TryGetValue(key, out test) && EqualityComparer.Default.Equals(comparisonValue, test)) { _store[key] = value; return true; } return false; } } /// /// Modify the value associated with the result of the provided update method /// as an atomic operation, Allows for reading/writing a single record within /// the tree lock. Be cautious about the behavior and performance of the code /// provided as it can cause a dead-lock to occur. If the method returns an /// instance who .Equals the original, no update is applied. /// public bool TryUpdate(TKey key, KeyValueUpdate fnUpdate) { using (_lock.Write()) { TValue value; if (_store.TryGetValue(key, out value)) { _store[key] = fnUpdate(key, value); return true; } return false; } } /// /// Removes the element with the specified key from the . /// /// /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . /// /// The key of the element to remove. /// The value that was removed. /// The is read-only. public bool TryRemove(TKey key, out TValue value) { using (_lock.Write()) { if (_store.TryGetValue(key, out value)) { _store.Remove(key); return true; } return false; } } /// /// Removes the element with the specified key from the /// if the fnCondition predicate is null or returns true. /// public bool TryRemove(TKey key, KeyValuePredicate fnCondition) { using (_lock.Write()) { TValue value; if (_store.TryGetValue(key, out value) && fnCondition(key, value)) { _store.Remove(key); return true; } return false; } } /// /// Conditionally removes a key/value pair from the dictionary via an implementation of the /// interface. /// public bool TryRemove(TKey key, ref T removeValue) where T : IRemoveValue { using (_lock.Write()) { TValue value; if (_store.TryGetValue(key, out value) && removeValue.RemoveValue(key, value)) { _store.Remove(key); return true; } return false; } } #endregion } }