I've been agonizing over this for an hour or two. I require a bidirectional dictionary that was thread safe. From what I understand about dictionaries, they're thread safe when being read from, but no so much when being written to. I've tried to write this code snippet to my understanding of correct, but a review would be excellent if possible.
class ConcurrentBidirectionalDictionary<TKey,UKey>
{
private Dictionary<TKey, UKey> BackingDictionary;
public ConcurrentBidirectionalDictionary()
{
BackingDictionary = new Dictionary<TKey, UKey>();
}
//Adding to dictionaries is not thread-safe ergo, lock when adding
private readonly object _SyncLock = new object();
public void Add(TKey item1, UKey item2)
{
if (!BackingDictionary.ContainsKey(item1) && !BackingDictionary.ContainsValue(item2))
{
lock (_SyncLock)
{
if (!BackingDictionary.ContainsKey(item1) && !BackingDictionary.ContainsValue(item2))
{
BackingDictionary.Add(item1, item2);
}
else
{
ThrowNewDuplicateKeyException(item1, item2);
}
}
}
else
{
ThrowNewDuplicateKeyException(item1, item2);
}
}
private void ThrowNewDuplicateKeyException(TKey item1, UKey item2)
{
if (BackingDictionary.ContainsKey(item1) && BackingDictionary.ContainsValue(item2))
{
throw new ArgumentException("Duplicate Keys not allowed", "item1, item 2", null);
}
else if (BackingDictionary.ContainsKey(item1))
{
throw new ArgumentException("Duplicate Keys not allowed", "item1", null);
}
else if (BackingDictionary.ContainsValue(item2))
{
throw new ArgumentException("Duplicate Keys not allowed", "item 2", null);
}
}
//Reading is thread safe, no lock needed
public TKey GetFromUKey(UKey UKey)
{
var Record = BackingDictionary.FirstOrDefault(x => EqualityComparer<UKey>.Default.Equals(x.Value, UKey));
if (Record.Equals(default(KeyValuePair<TKey,UKey>)))
{
throw new IndexOutOfRangeException($"Unable to find UKey {UKey}");
}
return Record.Key;
}
public UKey GetFromTKey(TKey TKey)
{
return BackingDictionary[TKey];
}
}