I find that I use this pattern a lot where I have a dictionary which I would only read from and update and wouldn't add/remove keys.
In that case, using ConcurrentDictionary
is not necessary and could hinder performance.
I wrote a simple class which implements such dictionary by using a wrapper class as its values:
class ConstantKeysDictionary<TKey, TValue> : IDictionary<TKey, TValue> where TValue : class
{
#region Internal Classes
class Wrapper
{
private TValue m_Val;
public TValue Value
{
get
{
return Volatile.Read<TValue>(ref m_Val);
}
set
{
Volatile.Write(ref m_Val, value);
}
}
}
#endregion
private Dictionary<TKey, Wrapper> m_InnerDictionary;
public ConstantKeysDictionary(IDictionary<TKey, TValue> toCopy)
{
m_InnerDictionary = new Dictionary<TKey, Wrapper>();
foreach (var pair in toCopy)
{
m_InnerDictionary.Add(pair.Key, new Wrapper() { Value = pair.Value });
}
}
public ConstantKeysDictionary(IEnumerable<TKey> keys)
{
m_InnerDictionary = new Dictionary<TKey, Wrapper>();
foreach (var key in keys)
{
m_InnerDictionary.Add(key, new Wrapper());
}
}
public ConstantKeysDictionary(Action<Dictionary<TKey, TValue>> builder)
{
Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
builder(dict);
m_InnerDictionary = new Dictionary<TKey, Wrapper>();
foreach (var pair in dict)
{
m_InnerDictionary.Add(pair.Key, new Wrapper() { Value = pair.Value });
}
}
public TValue this[TKey key]
{
get
{
return m_InnerDictionary[key].Value;
}
set
{
if (!m_InnerDictionary.ContainsKey(key))
{
throw new KeyNotFoundException();
}
m_InnerDictionary[key].Value = value;
}
}
public int Count
{
get
{
return m_InnerDictionary.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public ICollection<TKey> Keys
{
get
{
return m_InnerDictionary.Keys;
}
}
public ICollection<TValue> Values
{
get
{
return m_InnerDictionary.Values.Select(v => v.Value).ToList();
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException();
}
public void Add(TKey key, TValue value)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return ContainsKey(item.Key) &&
m_InnerDictionary[item.Key].Value.Equals(item.Value);
}
public bool ContainsKey(TKey key)
{
return m_InnerDictionary.ContainsKey(key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotSupportedException();
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (var pair in m_InnerDictionary)
{
yield return new KeyValuePair<TKey, TValue>(pair.Key, pair.Value.Value);
}
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
throw new NotSupportedException();
}
public bool Remove(TKey key)
{
throw new NotSupportedException();
}
public bool TryGetValue(TKey key, out TValue value)
{
Wrapper wrapper;
if (!m_InnerDictionary.TryGetValue(key, out wrapper))
{
value = default(TValue);
return false;
}
value = wrapper.Value;
return true;
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_InnerDictionary.GetEnumerator();
}
}
The point of this class is to be thread safe (given that you can't add/remove keys) without using locks/concurrent collections.
As you can see, many of the methods are not implemented because these are irrelevant for this kind of dictionary. In these methods, I throw NotSupportedException
.