I am trying to use ConcurrentDictionary to implement a cap limited cache. When the cache reaches its capacity, further additions of new items are rejected. The code snippet is as follows:
var result = this.cache.AddOrUpdate(
key,
(key1) =>
{
if (!this.IsFull())
{
return new List<MyObject> { value };
}
what to return here??
},
(key1, value1) =>
{
value1.Add(value);
return value1;
});
My question here if the cache is full what should I return here? null? or what?
You have two options:
Write a new concurrrent data struct to manage your cache constraints. This is harder but if such a struct is going to be used in several places within your solution thet go for this one.
Delegate cache's constraints management to the class that actually holds the cache. This is easier.
#Kirill Polishchuck answer is an example of the second alternative, but I belive it is not threadsafe so I would change it like so it ends up like:
if (!this.IsFull())
{
// your logic here
this.cache.AddOrUpdate(key, k=> new List<MyObject> { value };);
}
else
{
// logic
}
For the first alternative here is a sample of what you could do to implement it. We implement IDictonary<TKey,TValue> interface and we lock on those operations that require so. Also, the class does not allow further insertions if _maxCount has been surpassed:
public class MaxCountDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> _dictionary;
private readonly object _lock;
public MaxCountDictionary(int maxCount) : this(maxCount, EqualityComparer<TKey>.Default) { }
public MaxCountDictionary(int maxCount, IEqualityComparer<TKey> equalityComparer)
{
_lock = new object();
MaxCount = maxCount;
_dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
public int MaxCount { get; }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _dictionary.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
public void Clear()
{
lock (_lock)
{
_dictionary.Clear();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
lock (_lock)
{
return ((IDictionary<TKey, TValue>) _dictionary).Contains(item);
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
lock (_lock)
{
((IDictionary<TKey, TValue>) _dictionary).CopyTo(array, arrayIndex);
}
}
public bool Remove(KeyValuePair<TKey, TValue> item) => Remove(item.Key);
public int Count
{
get
{
lock (_lock)
{
return _dictionary.Count;
}
}
}
public bool IsReadOnly => ((IDictionary<TKey, TValue>) _dictionary).IsReadOnly;
public bool ContainsKey(TKey key)
{
lock (_lock)
{
return _dictionary.ContainsKey(key);
}
}
public void Add(TKey key, TValue value)
{
lock (_lock)
{
if (_dictionary.Count < MaxCount) _dictionary.Add(key, value);
}
}
public bool Remove(TKey key)
{
lock (_lock)
{
return _dictionary.Remove(key);
}
}
public bool TryGetValue(TKey key, out TValue value)
{
lock (_lock)
{
return _dictionary.TryGetValue(key, out value);
}
}
public TValue this[TKey key]
{
get
{
lock (_lock)
{
return _dictionary[key];
}
}
set
{
lock (_lock)
{
if (_dictionary.ContainsKey(key) || _dictionary.Count < MaxCount) _dictionary[key] = value;
}
}
}
public ICollection<TKey> Keys
{
get
{
lock (_lock)
{
return _dictionary.Keys.ToArray();
}
}
}
public ICollection<TValue> Values
{
get
{
lock (_lock)
{
return _dictionary.Values.ToArray();
}
}
}
public void AddOrUpdate(TKey key, TValue value, Func<TKey, TValue, TValue> updateValueFactory)
{
lock (_lock)
{
if (_dictionary.ContainsKey(key))
_dictionary[key] = updateValueFactory(key, value);
else if (_dictionary.Count < MaxCount) _dictionary[key] = value;
}
}
}
With this other alternative you simply pass a MaxCount parameter to your collection, you can change it to accept a Func<bool> so that the dictionary can figure out whether to add more items or not, this way you can pass your IsFull method to it.
Beware: This is demo code, it is just a sample, it is not thread safe to enumarate, adjust it to your needs.
Related
I made a class that uses a SortedDictionary to store and manipulate data. The class works great except when it is implemented in a multi-threaded environment. Now, I would like to make the class thread safe by writing a wrapper class for the internal SortedDictionary class. I would like to use the Reader-Writer Locks to implement this but for now, I'm having problems just writing the wrapper class itself. Specifically, I'm not sure how to implement the Enumerator for the dictionary. Here is my complete code for the class as it stands now.
public class ConcurrentSortedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
#region Variables
SortedDictionary<TKey, TValue> _dict;
#endregion
#region Constructors
public ConcurrentSortedDictionary()
{
_dict = new SortedDictionary<TKey, TValue>();
}
public ConcurrentSortedDictionary(IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(comparer);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary, comparer);
}
#endregion
#region Properties
public IComparer<TKey> Comparer
{
get
{
return _dict.Comparer;
}
}
public int Count
{
get
{
return _dict.Count;
}
}
public TValue this[TKey key]
{
get
{
return _dict[key];
}
set
{
_dict[key] = value;
}
}
public SortedDictionary<TKey, TValue>.KeyCollection Keys
{
get
{
return new SortedDictionary<TKey,TValue>.KeyCollection(_dict);
}
}
public SortedDictionary<TKey, TValue>.ValueCollection Values
{
get
{
return new SortedDictionary<TKey, TValue>.ValueCollection(_dict);
}
}
#endregion
#region Methods
public void Add(TKey key, TValue value)
{
_dict.Add(key, value);
}
public void Clear()
{
_dict.Clear();
}
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public bool ContainsValue(TValue value)
{
return _dict.ContainsValue(value);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
_dict.CopyTo(array, index);
}
public override bool Equals(Object obj)
{
return _dict.Equals(obj);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
public override int GetHashCode()
{
return _dict.GetHashCode();
}
public bool Remove(TKey key)
{
return _dict.Remove(key);
}
public override string ToString()
{
return _dict.ToString();
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dict.TryGetValue(key, out value);
}
#endregion
}
When I compile the code, I get the error message:
'ConcurrentSortedDictionary' does not implement interface
member 'System.Collections.IEnumerable.GetEnumerator()'.
'ConcurrentSortedDictionary.GetEnumerator()' cannot
implement 'System.Collections.IEnumerable.GetEnumerator()' because it
does not have the matching return type of
'System.Collections.IEnumerator'.
I looked at several posts here relating to this as a reference:
How do I implement IEnumerable in my Dictionary wrapper class that implements IEnumerable<Foo>?
What's the best way of implementing a thread-safe Dictionary?
But I don't see what I'm doing wrong. Any assistance greatly appreciated.
The problem is here:
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
You need:
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dict.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _dict.GetEnumerator();
}
The second non-generic GetEnumerator() is an explicit interface implementation and is needed as an unfortunate throwback to the days before generics and generic collections existed in C#.
See also: IEnumerable<T> provides two GetEnumerator methods - what is the difference between them? (and in particular Michael B's answer).
However if you want enumeration to be thread-safe along with the rest of your class, you may also need to write your own thread-safe IEnumerator type which cooperates with the reader/writer locks in your class!
After implementing the suggestion by dvnrrs, I now have the class working well. I even added a wrapper class for the IEnumerable interface to protect enumerations of the SortedDictionary (code modified from this example here: http://www.codeproject.com/Articles/56575/Thread-safe-enumeration-in-C). Here is the updated code with the Reader-Writer Locks included:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
public class SafeEnumerator<T> : IEnumerator<T>
{
#region Variables
// this is the (thread-unsafe)
// enumerator of the underlying collection
private readonly IEnumerator<T> _enumerator;
// this is the object we shall lock on.
private ReaderWriterLockSlim _lock;
#endregion
#region Constructor
public SafeEnumerator(IEnumerator<T> inner, ReaderWriterLockSlim readWriteLock)
{
_enumerator = inner;
_lock = readWriteLock;
// Enter lock in constructor
_lock.EnterReadLock();
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
// .. and exiting lock on Dispose()
// This will be called when the foreach loop finishes
_lock.ExitReadLock();
}
#endregion
#region Implementation of IEnumerator
// we just delegate actual implementation
// to the inner enumerator, that actually iterates
// over some collection
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset()
{
_enumerator.Reset();
}
public T Current
{
get
{
return _enumerator.Current;
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
#endregion
}
public class ConcurrentSortedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
#region Variables
private ReaderWriterLockSlim _readWriteLock = new ReaderWriterLockSlim();
SortedDictionary<TKey, TValue> _dict;
#endregion
#region Constructors
public ConcurrentSortedDictionary()
{
_dict = new SortedDictionary<TKey, TValue>();
}
public ConcurrentSortedDictionary(IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(comparer);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary);
}
public ConcurrentSortedDictionary(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(dictionary, comparer);
}
#endregion
#region Properties
public IComparer<TKey> Comparer
{
get
{
_readWriteLock.EnterReadLock();
try
{
return _dict.Comparer;
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
public int Count
{
get
{
_readWriteLock.EnterReadLock();
try
{
return _dict.Count;
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
public TValue this[TKey key]
{
get
{
_readWriteLock.EnterReadLock();
try
{
return _dict[key];
}
finally
{
_readWriteLock.ExitReadLock();
}
}
set
{
_readWriteLock.EnterWriteLock();
try
{
_dict[key] = value;
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
}
public SortedDictionary<TKey, TValue>.KeyCollection Keys
{
get
{
_readWriteLock.EnterReadLock();
try
{
return new SortedDictionary<TKey, TValue>.KeyCollection(_dict);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
public SortedDictionary<TKey, TValue>.ValueCollection Values
{
get
{
_readWriteLock.EnterReadLock();
try
{
return new SortedDictionary<TKey, TValue>.ValueCollection(_dict);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
}
#endregion
#region Methods
public void Add(TKey key, TValue value)
{
_readWriteLock.EnterWriteLock();
try
{
_dict.Add(key, value);
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public void Clear()
{
_readWriteLock.EnterWriteLock();
try
{
_dict.Clear();
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public bool ContainsKey(TKey key)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.ContainsKey(key);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public bool ContainsValue(TValue value)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.ContainsValue(value);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
_readWriteLock.EnterReadLock();
try
{
_dict.CopyTo(array, index);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public override bool Equals(Object obj)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.Equals(obj);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return new SafeEnumerator<KeyValuePair<TKey, TValue>>(_dict.GetEnumerator(), _readWriteLock);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new SafeEnumerator<KeyValuePair<TKey, TValue>>(_dict.GetEnumerator(), _readWriteLock);
}
public override int GetHashCode()
{
_readWriteLock.EnterReadLock();
try
{
return _dict.GetHashCode();
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public bool Remove(TKey key)
{
_readWriteLock.EnterWriteLock();
try
{
return _dict.Remove(key);
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public override string ToString()
{
_readWriteLock.EnterReadLock();
try
{
return _dict.ToString();
}
finally
{
_readWriteLock.ExitReadLock();
}
}
public bool TryGetValue(TKey key, out TValue value)
{
_readWriteLock.EnterReadLock();
try
{
return _dict.TryGetValue(key, out value);
}
finally
{
_readWriteLock.ExitReadLock();
}
}
#endregion
}
The .NET 4.0 ConditionalWeakTable<T> is effectively a dictionary where the dictionary's keys are weak referenced and can be collected, which is exactly what I need. The problem is that I need to be able to get all live keys from this dictionary, but MSDN states:
It does not include all the methods (such as GetEnumerator or
Contains) that a dictionary typically has.
Is there a possibility to retrieve the live keys or key-value pairs from a ConditionalWeakTable<T>?
I ended up creating my own wrapper:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
public sealed class ConditionalHashSet<T> where T : class
{
private readonly object locker = new object();
private readonly List<WeakReference> weakList = new List<WeakReference>();
private readonly ConditionalWeakTable<T, WeakReference> weakDictionary =
new ConditionalWeakTable<T, WeakReference>();
public void Add(T item)
{
lock (this.locker)
{
var reference = new WeakReference(item);
this.weakDictionary.Add(item, reference);
this.weakList.Add(reference);
this.Shrink();
}
}
public void Remove(T item)
{
lock (this.locker)
{
WeakReference reference;
if (this.weakDictionary.TryGetValue(item, out reference))
{
reference.Target = null;
this.weakDictionary.Remove(item);
}
}
}
public T[] ToArray()
{
lock (this.locker)
{
return (
from weakReference in this.weakList
let item = (T)weakReference.Target
where item != null
select item)
.ToArray();
}
}
private void Shrink()
{
// This method prevents the List<T> from growing indefinitely, but
// might also cause a performance problem in some cases.
if (this.weakList.Capacity == this.weakList.Count)
{
this.weakList.RemoveAll(weak => !weak.IsAlive);
}
}
}
In some recent framework version, the ConditionalWeakTable<TKey,TValue> now implements IEnumerator interface. Check out Microsoft Docs.
This applies to
.NET Core >= 2.0
.NET Standard >= 2.1
This is not solving the problem if someone is stuck with .NET Framework. Otherwise, this may help if, like me, it's only a matter of updating from .NET Standard 2.0 to 2.1.
This will work without the performance problems.
The key to the problem is to use a "holder" object as a value in the ConditionalWeakTable, so that when the key gets dropped, the holder's finalizer will trigger, which removes the key from the "active list" of keys.
I tested this and it works.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Util
{
public class WeakDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDisposable
where TKey : class
where TValue : class
{
private readonly object locker = new object();
//private readonly HashSet<WeakReference> weakKeySet = new HashSet<WeakReference>(new ObjectReferenceEqualityComparer<WeakReference>());
private ConditionalWeakTable<TKey, WeakKeyHolder> keyHolderMap = new ConditionalWeakTable<TKey, WeakKeyHolder>();
private Dictionary<WeakReference, TValue> valueMap = new Dictionary<WeakReference, TValue>(new ObjectReferenceEqualityComparer<WeakReference>());
private class WeakKeyHolder
{
private WeakDictionary<TKey, TValue> outer;
private WeakReference keyRef;
public WeakKeyHolder(WeakDictionary<TKey, TValue> outer, TKey key)
{
this.outer = outer;
this.WeakRef = new WeakReference(key);
}
public WeakReference WeakRef { get; private set; }
~WeakKeyHolder()
{
this.outer?.onKeyDrop(this.WeakRef); // Nullable operator used just in case this.outer gets set to null by GC before this finalizer runs. But I haven't had this happen.
}
}
private void onKeyDrop(WeakReference weakKeyRef)
{
lock(this.locker)
{
if (!this.bAlive)
return;
//this.weakKeySet.Remove(weakKeyRef);
this.valueMap.Remove(weakKeyRef);
}
}
// The reason for this is in case (for some reason which I have never seen) the finalizer trigger doesn't work
// There is not much performance penalty with this, since this is only called in cases when we would be enumerating the inner collections anyway.
private void manualShrink()
{
var keysToRemove = this.valueMap.Keys.Where(k => !k.IsAlive).ToList();
foreach (var key in keysToRemove)
valueMap.Remove(key);
}
private Dictionary<TKey, TValue> currentDictionary
{
get
{
lock(this.locker)
{
this.manualShrink();
return this.valueMap.ToDictionary(p => (TKey) p.Key.Target, p => p.Value);
}
}
}
public TValue this[TKey key]
{
get
{
if (this.TryGetValue(key, out var val))
return val;
throw new KeyNotFoundException();
}
set
{
this.set(key, value, isUpdateOkay: true);
}
}
private bool set(TKey key, TValue val, bool isUpdateOkay)
{
lock (this.locker)
{
if (this.keyHolderMap.TryGetValue(key, out var weakKeyHolder))
{
if (!isUpdateOkay)
return false;
this.valueMap[weakKeyHolder.WeakRef] = val;
return true;
}
weakKeyHolder = new WeakKeyHolder(this, key);
this.keyHolderMap.Add(key, weakKeyHolder);
//this.weakKeySet.Add(weakKeyHolder.WeakRef);
this.valueMap.Add(weakKeyHolder.WeakRef, val);
return true;
}
}
public ICollection<TKey> Keys
{
get
{
lock(this.locker)
{
this.manualShrink();
return this.valueMap.Keys.Select(k => (TKey) k.Target).ToList();
}
}
}
public ICollection<TValue> Values
{
get
{
lock (this.locker)
{
this.manualShrink();
return this.valueMap.Select(p => p.Value).ToList();
}
}
}
public int Count
{
get
{
lock (this.locker)
{
this.manualShrink();
return this.valueMap.Count;
}
}
}
public bool IsReadOnly => false;
public void Add(TKey key, TValue value)
{
if (!this.set(key, value, isUpdateOkay: false))
throw new ArgumentException("Key already exists");
}
public void Add(KeyValuePair<TKey, TValue> item)
{
this.Add(item.Key, item.Value);
}
public void Clear()
{
lock(this.locker)
{
this.keyHolderMap = new ConditionalWeakTable<TKey, WeakKeyHolder>();
this.valueMap.Clear();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
WeakKeyHolder weakKeyHolder = null;
object curVal = null;
lock (this.locker)
{
if (!this.keyHolderMap.TryGetValue(item.Key, out weakKeyHolder))
return false;
curVal = weakKeyHolder.WeakRef.Target;
}
return (curVal?.Equals(item.Value) == true);
}
public bool ContainsKey(TKey key)
{
lock (this.locker)
{
return this.keyHolderMap.TryGetValue(key, out var weakKeyHolder);
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((IDictionary<TKey, TValue>) this.currentDictionary).CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return this.currentDictionary.GetEnumerator();
}
public bool Remove(TKey key)
{
lock (this.locker)
{
if (!this.keyHolderMap.TryGetValue(key, out var weakKeyHolder))
return false;
this.keyHolderMap.Remove(key);
this.valueMap.Remove(weakKeyHolder.WeakRef);
return true;
}
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
lock (this.locker)
{
if (!this.keyHolderMap.TryGetValue(item.Key, out var weakKeyHolder))
return false;
if (weakKeyHolder.WeakRef.Target?.Equals(item.Value) != true)
return false;
this.keyHolderMap.Remove(item.Key);
this.valueMap.Remove(weakKeyHolder.WeakRef);
return true;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
lock (this.locker)
{
if (!this.keyHolderMap.TryGetValue(key, out var weakKeyHolder))
{
value = default(TValue);
return false;
}
value = this.valueMap[weakKeyHolder.WeakRef];
return true;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private bool bAlive = true;
public void Dispose()
{
this.Dispose(true);
}
protected void Dispose(bool bManual)
{
if (bManual)
{
Monitor.Enter(this.locker);
if (!this.bAlive)
return;
}
try
{
this.keyHolderMap = null;
this.valueMap = null;
this.bAlive = false;
}
finally
{
if (bManual)
Monitor.Exit(this.locker);
}
}
~WeakDictionary()
{
this.Dispose(false);
}
}
public class ObjectReferenceEqualityComparer<T> : IEqualityComparer<T>
{
public static ObjectReferenceEqualityComparer<T> Default = new ObjectReferenceEqualityComparer<T>();
public bool Equals(T x, T y)
{
return ReferenceEquals(x, y);
}
public int GetHashCode(T obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
}
public class ObjectReferenceEqualityComparer : ObjectReferenceEqualityComparer<object>
{
}
}
I'm completely new to C#, so I'm about to make a horrible attempt at my own version of an OrderedDictionary unless someone can suggest an alternative.
I need to be able to access my elements by array index, retaining the order they were added, and I also will be frequently updating individual elements using their key.
Is there a collection that allows this on the phone?
If I keep a List and Dictionary will they both be pointing to the same item or is there some kind of pointer thing I have to do?:
Item i = new Item();
list.Add(i);
dict.Add("key", i);
Here's my implementation (comes from the open source OpenNETCF Extensions library):
public class OrderedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
private Dictionary<TKey, TValue> m_dictionary;
private List<TValue> m_list = new List<TValue>();
private object m_syncRoot = new object();
public OrderedDictionary()
{
m_dictionary = new Dictionary<TKey, TValue>();
}
public OrderedDictionary(IEqualityComparer<TKey> comparer)
{
m_dictionary = new Dictionary<TKey, TValue>(comparer);
}
public void Add(TKey key, TValue value)
{
lock (m_syncRoot)
{
m_dictionary.Add(key, value);
m_list.Add(value);
}
}
public TValue this[int index]
{
get { return m_list[index]; }
}
public TValue this[TKey key]
{
get { return m_dictionary[key]; }
}
public int Count
{
get { return m_dictionary.Count; }
}
public Dictionary<TKey, TValue>.KeyCollection Keys
{
get { return m_dictionary.Keys; }
}
public Dictionary<TKey, TValue>.ValueCollection Values
{
get { return m_dictionary.Values; }
}
public void Clear()
{
lock (m_syncRoot)
{
m_dictionary.Clear();
m_list.Clear();
}
}
public bool ContainsKey(TKey key)
{
return m_dictionary.ContainsKey(key);
}
public bool ContainsValue(TValue value)
{
return m_dictionary.ContainsValue(value);
}
public void Insert(int index, TKey key, TValue value)
{
lock (m_syncRoot)
{
m_list.Insert(index, value);
m_dictionary.Add(key, value);
}
}
public void Remove(TKey key)
{
lock (m_syncRoot)
{
if (ContainsKey(key))
{
var existing = m_dictionary[key];
m_list.Remove(existing);
m_dictionary.Remove(key);
}
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Using a List and a Dictionary is probably a good option actually. The "pointer thing" that you're talking about happens by default for Objects in .NET (any class and/or structure). All objects in .NET are passed around by reference.
So, if you use:
Item i = new Item();
list.Add(i);
dict.Add("key",i);
Console.WriteLine(list.Last() == dict["key"]);
Your output will be "true".
Best of luck!
I won't suggest using OrderedDictionary, since it's a non-generic container.
However, if you just want to use it like always. You can port Mono's version of OrderedDictionary.
https://github.com/mono/mono/blob/master/mcs/class/System/System.Collections.Specialized/OrderedDictionary.cs
Here's some tips if you want to port this:
Remove any unavailable interface
Remove serialization-related code
Replace ArrayList with List<object>
Replace Hashtable with Dictionary<object, object>
I require a thread safe collection but are unable to use ConcurrentDictionary as its .NET 4.0, and I need to use .NET 3.5. What are alternatives are out there?
In the end I found what I was after. A nuget package of the TPL backported to 3.5. It can be found here
http://nuget.org/packages/TaskParallelLibrary
Have a look at this article
“Thread safe” Dictionary(TKey,TValue)
For Framework 3.5 I see the following options:
Dictionary + Monitor. Simple wrap to lock
Dictionary + ReaderWriterLock. Better than the previous one, because has got read and write locks. So several threads can read, and just one - write.
Dictionary + ReaderWriterLockSlim. It's just optimisation of the previous one.
Hashtable. From my experience, it's the slowest method. Check Hashtable.Synchronized() method, it's a ready to go solution from Microsoft.
I would use Dictionary + ReaderWriterLock or ReaderWriterLockSlim.
You can create a simple wrapper and only implement what you need. Keep in mind that it is not thread safe to perform an operation on a Dictionary that relies on the result of a previous call. This is exemplified in the TryAdd method.
class ConcurrentMap<K, V>
{
readonly Dictionary<K, V> _map = new Dictionary<K, V>();
public bool TryGetValue(K key, out V value)
{
lock (_map)
{
return _map.TryGetValue(key, out value);
}
}
public bool TryAdd(K key, V value)
{
lock (_map)
{
if (!_map.ContainsKey(key))
{
_map.Add(key, value);
return true;
}
return false;
}
}
public bool TryRemove(K key)
{
lock (_map)
{
return _map.Remove(key);
}
}
}
I just needed that for .Net 3.5... someone's still there back :)
adding my class, added some more functionality than previous attached codes.
public class SafeDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
private readonly object _Padlock = new object();
private readonly Dictionary<TKey, TValue> _Dictionary = new Dictionary<TKey, TValue>();
public TValue this[TKey key]
{
get
{
lock (_Padlock)
{
return _Dictionary[key];
}
}
set
{
lock (_Padlock)
{
_Dictionary[key] = value;
}
}
}
public bool TryGetValue(TKey key, out TValue value)
{
lock (_Padlock)
return _Dictionary.TryGetValue(key, out value);
}
public bool TryAdd(TKey key, TValue value)
{
lock (_Padlock)
{
if (!_Dictionary.ContainsKey(key))
{
_Dictionary.Add(key, value);
return true;
}
return false;
}
}
public bool TryRemove(TKey key)
{
lock (_Padlock)
{
return _dictionary.Remove(key);
}
}
}
internal void Add(TKey key, TValue val)
{
lock (_Padlock)
{
_Dictionary.Add(key, val);
}
}
public bool ContainsKey(TKey id)
{
lock (_Padlock)
return _Dictionary.ContainsKey(id);
}
public List<KeyValuePair<TKey, TValue>> OrderBy(Func<KeyValuePair<TKey, TValue>, TKey> func)
{
lock (_Padlock)
return _Dictionary.OrderBy(func).ToList();
}
public Dictionary<TKey, TValue>.ValueCollection Values
{
get
{
lock (_Padlock)
return _Dictionary.Values;
}
}
public Dictionary<TKey, TValue>.KeyCollection Keys
{
get
{
lock (_Padlock)
return _Dictionary.Keys;
}
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
lock (_Padlock)
return _Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
lock (_Padlock)
return _Dictionary.GetEnumerator();
}
}
I thought of solution below because the collection is very very small. But what if it was big?
private Dictionary<string, OfTable> _folderData = new Dictionary<string, OfTable>();
public Dictionary<string, OfTable> FolderData
{
get { return new Dictionary<string,OfTable>(_folderData); }
}
With List you can make:
public class MyClass
{
private List<int> _items = new List<int>();
public IList<int> Items
{
get { return _items.AsReadOnly(); }
}
}
That would be nice!
Thanks in advance, Cheers & BR - Matti
NOW WHEN I THINK THE OBJECTS IN COLLECTION ARE IN HEAP. SO MY SOLUTION DOES NOT PREVENT THE CALLER TO MODIFY THEM!!! CAUSE BOTH Dictionary s CONTAIN REFERENCES TO SAME OBJECT. DOES THIS APPLY TO List EXAMPLE ABOVE?
class OfTable
{
private int _table;
private List<int> _classes;
private string _label;
public OfTable()
{
_classes = new List<int>();
}
public int Table
{
get { return _table; }
set { _table = value; }
}
public List<int> Classes
{
get { return _classes; }
set { _classes = value; }
}
public string Label
{
get { return _label; }
set { _label = value; }
}
}
so how to make this immutable??
It's not difficult to roll your own ReadOnlyDictionary<K,V> wrapper class. Something like this:
public sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> _dictionary;
public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}
public bool ContainsKey(TKey key)
{
return _dictionary.ContainsKey(key);
}
public int Count
{
get { return _dictionary.Count; }
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dictionary.GetEnumerator();
}
public ICollection<TKey> Keys
{
get { return _dictionary.Keys; }
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return _dictionary.Values; }
}
public TValue this[TKey key] // Item
{
get { return _dictionary[key]; }
}
#region IDictionary<TKey, TValue> Explicit Interface Implementation
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
throw new NotSupportedException("Dictionary is read-only.");
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
throw new NotSupportedException("Dictionary is read-only.");
}
TValue IDictionary<TKey, TValue>.this[TKey key] // Item
{
get { return _dictionary[key]; }
set { throw new NotSupportedException("Dictionary is read-only."); }
}
#endregion
#region ICollection<T> Explicit Interface Implementation
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException("Collection is read-only.");
}
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
{
throw new NotSupportedException("Collection is read-only.");
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return _dictionary.Contains(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dictionary.CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
{
get { return true; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException("Collection is read-only.");
}
#endregion
#region IEnumerable Explicit Interface Implementation
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_dictionary).GetEnumerator();
}
#endregion
}
If you're using C#3 or later then you could knock-up a matching AsReadOnly extension method too:
public static class ReadOnlyDictionaryHelper
{
public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
{
var temp = dictionary as ReadOnlyDictionary<TKey, TValue>;
return temp ?? new ReadOnlyDictionary<TKey, TValue>(dictionary);
}
}
And then return the read-only wrapper from your property:
// in C#2
return new ReadOnlyDictionary<string, OfTable>(_folderData);
// in C#3 or later
return _folderData.AsReadOnly();
Use ReadOnlyCollection<T> class.
An instance of the ReadOnlyCollection generic class is always read-only. A collection that is read-only is simply a collection with a wrapper that prevents modifying the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes. See Collection for a modifiable version of this class.
--EDIT--
Checkout a trivial dictionary wrapper here. And A Generic Read-Only Dictionary by Richard Carr.