#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
}
}