I've seen posts here on how to make a dictionary that has multiple values per key, like one of the solutions presented in this link:
Multi Value Dictionary
it seems that I have to use a List<> as the value for the keys, so that a key can store multiple values.
the solution in the link is fine if you want to add values. But my problem now is how to remove specific values from a single key.
I have this code for adding values to a dictionary:
private Dictionary<TKey, List<TValue>> mEventDict;
// this is for initializing the dictionary
public void Subscribe(eVtEvtId inEvent, VtEvtDelegate inCallbackMethod)
{
if (mEventDict.ContainsKey(inEvent))
{
mEventDict[inEvent].Add(inCallbackMethod);
}
else
{
mEventDict.Add(inEvent, new List<TValue>() { v });
}
}
// this is for adding values to the dictionary.
// if the "key" (inEvent) is not yet present in the dictionary,
// the key will be added first before the value
my problem now is removing a specific value from a key. I have this code:
public void Unsubscribe(eVtEvtId inEvent, VtEvtDelegate inCallbackMethod)
{
try
{
mEventDict[inEvent].Remove(inCallbackMethod);
}
catch (ArgumentNullException)
{
MessageBox.Show("The event is not yet present in the dictionary");
}
}
basically, what I did is just replace the Add() with Remove() . Will this work?
Also, if you have any problems or questions with the code (initialization, etc.), feel free to ask.
Thanks for the advice.
TylerOhlsen's answer is a step in the right direction, but it has 6 key lookups (calls to Remove, ContainsKey, and the indexer). This can be reduced to three by using TryGetValue:
private Dictionary<TKey, List<TValue>> mEventDict;
public void Subscribe(TKey inEvent, TValue inCallbackMethod)
{
List<TValue> list;
if (mEventDict.TryGetValue(inEvent, out list))
list.Add(inCallbackMethod);
else
mEventDict.Add(inEvent, new List<TValue> { inCallbackMethod });
}
public bool Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
List<TValue> list;
if (!mEventDict.TryGetValue(inEvent, out list))
return false;
bool removed = list.Remove(inCallbackMethod);
if (list.Count == 0)
mEventDict.Remove(inEvent);
return removed;
}
If you don't care about removing empty lists:
public bool Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
List<TValue> list;
if (!mEventDict.TryGetValue(inEvent, out list))
return false;
return list.Remove(inCallbackMethod);
}
If you don't need to report whether the item was in the list (and therefore removed from it), change the return type to void, and (in the first version) get rid of the removed variable.
Will it work? Not exactly the way you intended...
Your method parameters will need to be of the generic types.
List(T).Remove does not throw an ArgumentNullException.
You might want to clean up your dictionary if your list becomes empty.
The caller might not care if the callback was ever subscribed when they unsubscribe, but you have that information so you might as well return it. This information could be helpful for troubleshooting/logging purposes.
This is what I would recommend...
private Dictionary<TKey, List<TValue>> mEventDict;
public void Subscribe(TKey inEvent, TValue inCallbackMethod)
{
if (!mEventDict.ContainsKey(inEvent))
mEventDict.Add(inEvent, new List<TValue>());
mEventDict[inEvent].Add(inCallbackMethod);
}
public bool Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
if (!mEventDict.ContainsKey(inEvent))
return false;
bool removed = mEventDict[inEvent].Remove(inCallbackMethod);
if (mEventDict[inEvent].Count == 0)
mEventDict.Remove(inEvent);
return removed;
}
NOTE: I have not tested this code, so just try it out. Also, this code is not thread safe.
#phoog - so I want to keep the Unsubscribe method as void . After modifying your code, this is what I came up with...
public void Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
List<TValue> list;
bool mRemoved = false.
if (mEventDict.TryGetValue(inEvent, out list))
{
list.Remove(inCallbackMethod);
mRemoved = true;
}
}
is the listRemoved variable necessary? But then again, I think nothing will happen if the inCallbackMethod cannot be found in the list.
Related
ConcurrentDictionary works well for concurrent situations when mapping keys to a single value each. When mapping to multiple values, it is easy to create a ConcurrentDictionary<K, List<V>> and guard its addition/removal functions.
ConcurrentDictionary <string, List<string>> d;
// Add
var list = d.GetOrAdd ("key", x => new List<string> ());
lock (list) {
list.Add ("value to add");
}
// Remove
if (d.TryGetValue ("key", out var list)) {
lock (list) {
list.Remove ("value to remove");
}
}
However, the above assumed that empty lists are allowed to stay. I don't want that. But removing empty pairs does not seem to be possible in an atomic fashion. One might try:
if (d.TryGetValue ("key", out var list)) {
lock (list) {
if (list.Remove ("value to remove") && list.Count == 0) {
d.TryRemove ("key", out _);
}
}
}
But this has a race condition when another thread grabs the list before but adds to it after it was emptied and removed elsewhere:
A: get list
B: get list
B: lock, remove from list
B: list is empty, delete key, unlock
A: lock, add to list, unlock
Locking on the dictionary is not possible (it's a different use case).
As far as I can tell, a solution would usually be found using compare-and-swap operations and replacing the list with e.g. an immutable array that is then replaced in its entirety. However, given that ConcurrentDictionary does not offer a TryRemove with an expected value to compare against, I don't quite see how. Possibly there is a two-stage solution?
Using the out parameter of TryRemove to add values again after removing them (to fix race cases) is not possible - the dictionary would briefly be in an inconsistent state.
There are many questions on this site asking about similar scenarios, but most of them suffer from trivial mistakes or do not remove empty entries. There is this highly related question which asks if it is possible to do this. Sadly, it is five years old, received very little attention and has no solution apart from resorting to locks (which defeats the purpose). Possibly there opened up a better way since that time.
(Edited example for clarity)
I managed to implement a ConcurrentMultiDictionary class that stores multiple values per key, and with empty entries removed. The values of each key are stored in a HashSet, so each key has unique values. This increases the performance of deleting a value when the number of values is large. If the uniqueness is a problem then the HashSet should be replaced with a List, and the Add method should be modified to return void instead of bool.
The atomicity of the adding and removing operations is achieved by spinning. When a bag of values becomes empty, it is flagged as "discarded". Adding values into a discarded bag is not allowed, so the Add operation spins until it grabs a non discarded bag. The Remove operation spins too. So the only thread that is allowed to remove a discarded bag is the same thread that marked the bag as discarded. All other threads will be spinning until that happens. SpinWait structs are used for the spinning, to ensure efficiency even in single processor machines.
An unsolvable problem of this implementation is how to implement a ToArray method that takes a snapshot of all keys and values stored in the dictionary. The ConcurrentDictionary.ToArray method returns a snapshot of the keys, but the bags can be constantly changing, and this is why I believe it is unsolvable.
Even implementing the IEnumerable interface is a bit tricky, because if we just enumerate the KeyValuePairs of the underlying dictionary, most of the bags could be discarded at the time their values are acquired. So during the enumeration the bag of each key is retrieved individually, to be as current as possible.
public class ConcurrentMultiDictionary<TKey, TValue>
: IEnumerable<KeyValuePair<TKey, TValue[]>>
{
private class Bag : HashSet<TValue>
{
public bool IsDiscarded { get; set; }
}
private readonly ConcurrentDictionary<TKey, Bag> _dictionary;
public ConcurrentMultiDictionary()
{
_dictionary = new ConcurrentDictionary<TKey, Bag>();
}
public int Count => _dictionary.Count;
public bool Add(TKey key, TValue value)
{
var spinWait = new SpinWait();
while (true)
{
var bag = _dictionary.GetOrAdd(key, _ => new Bag());
lock (bag)
{
if (!bag.IsDiscarded) return bag.Add(value);
}
spinWait.SpinOnce();
}
}
public bool Remove(TKey key, TValue value)
{
var spinWait = new SpinWait();
while (true)
{
if (!_dictionary.TryGetValue(key, out var bag)) return false;
bool spinAndRetry = false;
lock (bag)
{
if (bag.IsDiscarded)
{
spinAndRetry = true;
}
else
{
bool valueRemoved = bag.Remove(value);
if (!valueRemoved) return false;
if (bag.Count != 0) return true;
bag.IsDiscarded = true;
}
}
if (spinAndRetry) { spinWait.SpinOnce(); continue; }
bool keyRemoved = _dictionary.TryRemove(key, out var currentBag);
Debug.Assert(keyRemoved, $"Key {key} was not removed");
Debug.Assert(bag == currentBag, $"Removed wrong bag");
return true;
}
}
public bool TryGetValues(TKey key, out TValue[] values)
{
if (!_dictionary.TryGetValue(key, out var bag)) { values = null; return false; }
bool isDiscarded;
lock (bag) { isDiscarded = bag.IsDiscarded; values = bag.ToArray(); }
if (isDiscarded) { values = null; return false; }
return true;
}
public bool Contains(TKey key, TValue value)
{
if (!_dictionary.TryGetValue(key, out var bag)) return false;
lock (bag) return !bag.IsDiscarded && bag.Contains(value);
}
public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key);
public ICollection<TKey> Keys => _dictionary.Keys;
public IEnumerator<KeyValuePair<TKey, TValue[]>> GetEnumerator()
{
foreach (var key in _dictionary.Keys)
{
if (this.TryGetValues(key, out var values))
{
yield return new KeyValuePair<TKey, TValue[]>(key, values);
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
This implementation was tested with 8 concurrent workers mutating a dictionary a million times per worker, and no inconsistency regarding the reported number of additions and removals was observed.
There seems to be no practical way of removing an empty collection (even if it is synchronized) from a concurrent dictionary without having race condition issues. There are certain facts preventing this from being possible, as discussed in the comments under both the question and the OP's self answer.
What I wrote in my comment, however, seemed feasible and I wanted to give it a try.
I want to discuss the drawbacks of this implementation right after, and I should also say that your comments (if received any) are what is most valuable to me.
First, the usage:
static void Main(string[] args)
{
var myDictionary = new ConcurrentDictionary<string, IList<int>>();
IList<int> myList = myDictionary.AddSelfRemovingList<string, int>("myList");
myList.Add(5);
myList.Add(6);
myList.Remove(6);
myList.Remove(5);
IList<int> existingInstance;
// returns false:
bool exists = myDictionary.TryGetValue("myList", out existingInstance);
// throws HasAlreadyRemovedSelfException:
myList.Add(3);
}
AddSelfRemovingList is an extension method to make things easier.
For the discussion part:
It is not acceptable for the removal of an item from a collection to have a side effect of removing the collection reference from the owning dictionary.
It is also not good practice to make the collection obsolete (unusable) when all its items are removed. There is a strong possibility that the consumer of the collection wants to clear and re-fill the collection and this implementation does not allow that.
It forces the use of IList<T> abstraction and a custom implementation over List<T>
Although this provides a real thread-safe way of removing a just emptied collection from the dictionary, there seem to be more cons than pros to it. This should only be used in a closed context where the collections inside the concurrent dictionary are exposed to the outside, and where the immediate removal of a collection when emptied, even if some other thread is accessing it at the moment, is essential.
Here is the extension method to create and add the self removing list to the dictionary:
public static class ConcurrentDictionaryExtensions
{
public static IList<TValue> AddSelfRemovingList<TKey, TValue>(this ConcurrentDictionary<TKey, IList<TValue>> dictionaryInstance, TKey key)
{
var newInstance = new SelfRemovingConcurrentList<TKey, TValue>(dictionaryInstance, key);
if (!dictionaryInstance.TryAdd(key, newInstance))
{
throw new ArgumentException("ownerAccessKey", "The passed ownerAccessKey has already exist in the parent dictionary");
}
return newInstance;
}
}
And finally; here is the synchronized, self-removing implementation of IList<T>:
public class SelfRemovingConcurrentList<TKey, TValue> : IList<TValue>
{
private ConcurrentDictionary<TKey, IList<TValue>> owner;
private TKey ownerAccessKey;
List<TValue> underlyingList = new List<TValue>();
private bool hasRemovedSelf;
public class HasAlreadyRemovedSelfException : Exception
{
}
internal SelfRemovingConcurrentList(ConcurrentDictionary<TKey, IList<TValue>> owner, TKey ownerAccessKey)
{
this.owner = owner;
this.ownerAccessKey = ownerAccessKey;
}
private void ThrowIfHasAlreadyRemovedSelf()
{
if (hasRemovedSelf)
{
throw new HasAlreadyRemovedSelfException();
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
int IList<TValue>.IndexOf(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.IndexOf(item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void IList<TValue>.Insert(int index, TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.Insert(index, item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void IList<TValue>.RemoveAt(int index)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.RemoveAt(index);
if (underlyingList.Count == 0)
{
hasRemovedSelf = true;
IList<TValue> removedInstance;
if (!owner.TryRemove(ownerAccessKey, out removedInstance))
{
// Just ignore.
// What we want to do is to remove ourself from the owner (concurrent dictionary)
// and it seems like we have already been removed!
}
}
}
TValue IList<TValue>.this[int index]
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList[index];
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList[index] = value;
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
void ICollection<TValue>.Add(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.Add(item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void ICollection<TValue>.Clear()
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.Clear();
hasRemovedSelf = true;
IList<TValue> removedInstance;
if (!owner.TryRemove(ownerAccessKey, out removedInstance))
{
// Just ignore.
// What we want to do is to remove ourself from the owner (concurrent dictionary)
// and it seems like we have already been removed!
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
bool ICollection<TValue>.Contains(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.Contains(item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void ICollection<TValue>.CopyTo(TValue[] array, int arrayIndex)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.CopyTo(array, arrayIndex);
}
int ICollection<TValue>.Count
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.Count;
}
}
bool ICollection<TValue>.IsReadOnly
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
ThrowIfHasAlreadyRemovedSelf();
return false;
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
bool ICollection<TValue>.Remove(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
bool removalResult = underlyingList.Remove(item);
if (underlyingList.Count == 0)
{
hasRemovedSelf = true;
IList<TValue> removedInstance;
if (!owner.TryRemove(ownerAccessKey, out removedInstance))
{
// Just ignore.
// What we want to do is to remove ourself from the owner (concurrent dictionary)
// and it seems like we have already been removed!
}
}
return removalResult;
}
[MethodImpl(MethodImplOptions.Synchronized)]
IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.GetEnumerator();
}
[MethodImpl(MethodImplOptions.Synchronized)]
IEnumerator IEnumerable.GetEnumerator()
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.GetEnumerator();
}
}
The question can be solved by using a dictionary that offers a variant of TryRemove that first checks that the current value is equal to an expected value. Only if the values compare equal, the value is replaced (atomically). Otherwise, the operation returns failure.
It turns out ConcurrentDictionary already implements exactly this functionality:
/// <summary>
/// Removes the specified key from the dictionary if it exists and returns its associated value.
/// If matchValue flag is set, the key will be removed only if is associated with a particular
/// value.
/// </summary>
/// <param name="key">The key to search for and remove if it exists.</param>
/// <param name="value">The variable into which the removed value, if found, is stored.</param>
/// <param name="matchValue">Whether removal of the key is conditional on its value.</param>
/// <param name="oldValue">The conditional value to compare against if <paramref name="matchValue"/> is true</param>
/// <returns></returns>
private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue)
TryRemove calls this (with matchValue set to false). The method is sadly not exposed (it is private). A simple solution would thus be to copy the existing class and change this method to be public. I'm not sure why it was not exposed. If the specific functionality were not working well, matchValue would most likely have been removed earlier.
As #Theodor Zoulias notes, it is also possible to invoke the private TryRemoveInternal method by using reflection. As far as I know, this can be done without major impact on performence.
There are also third party implementations with (claimed) high performance and concurrency that exhibit a TryRemove (..., expectedValue).
Once an implementation is chosen, the following code implements the asked for functionality. It uses the atomic compare-and-swap operations provided by the dictionary in a loop until it succeeds (similar to what many concurrent dictionaries do internally, too). As far as I'm aware, this is a typical approach in lock-free algorithms.
// Use any third-party dictionary that offers TryRemove() with
// a value to compare against (two are mentioned above)
ConcurrentDictionary<TKey, List<TValue>> d;
...
// To remove a value from key:
// Loop until the compare-and-swap of either update or removal succeeded
while (true)
{
// If the key does not exist, exit
if (!d.TryGetValue (key, out var list)) {
break;
}
// Remove the value from this key's entry:
// Consider the old value immutable, copy-and-modify it instead
List<TValue> newlist;
lock (list) {
newlist = list.Where (it => it != valueToRemove).ToList ();
}
// If the value list is not empty, compare-and-update it
if (newlist.Count > 0) {
if (d.TryUpdate (key: key, newValue: newlist, expectedValue: list)) {
return;
}
}
else // The key's value list is empty - compare-and-remove the entire key
{
// Remove the key iff the associated value is still the same
if (d.TryRemove (key: key, expectedValue: list)) { // Note that list is an in-, not an out-parameter
return;
}
}
// If we reach this point, the operation failed - try again
}
This question already has answers here:
How to achieve remove_if functionality in .NET ConcurrentDictionary
(5 answers)
Closed 10 months ago.
The scenario I have is I want a method on ConcurrentDictionary like this.
bool TryRemove(TKey key, TValue value) {
// remove the value IF the value passed in == dictionary[key]
// return false if the key is not in the dictionary, or the value is not equal
}
Is there a way to do this concurrently? I'm struggling to find an answer for this scenario, even though it seems like this is a common use case.
I could do something like this, but I want to avoid a lock if I'm already using a ConcurrentDictionary. I'd also have to have locks on GetOrAdd() or AddOrUpdate() calls elsewhere. It just seems like there should be a better way with a ConcurrentDictionary.
ConcurrentDictionary<int, string> dict = ...;
/// stuff
int keyTryToRemove = 1337;
string valTryToRemove = "someValue";
bool success = false;
lock(keyTryToRemove) {
string val;
if (dict.TryRemove(keyTryToRemove, out val)) {
if (val == valTryToRemove) {
success = true;
}
else { // reinsert value, UGLY!
dict[keyTryToRemove] = val;
success = false;
}
} else {
success = false;
}
}
Since ConcurrentDictionary<TKey, TValue> class implements (although explicitly) IDictionary<TKey, TValue>, thus ICollection<KeyValuePair<TKey, TValue>>, you can simply cast it to the later and use Remove method like this:
bool success = ((ICollection<KeyValuePair<TKey, TValue>>)dict).Remove(
new KeyValuePair<TKey, TValue>(key, value));
The implementation internally uses the same thread safe method (passing additionally the value to be checked) as the public TryRemove method - exactly as it should be.
Edit: Generally speaking, the method in question can be made available for any type implementing IDictionary<TKey, TValue> (or more precisely ICollection<KeyValuePair<TKey, TValue>>) like Dictionary, ConcurrentDictionary etc. by introducing a custom extension method like this:
public static class Extensions
{
public static bool TryRemove<TKey, TValue>(this ICollection<KeyValuePair<TKey, TValue>> source, TKey key, TValue value)
{
return source.Remove(new KeyValuePair<TKey, TValue>(key, value));
}
}
so the sample code becomes simply:
bool success = dict.TryRemove(key, value);
I'd do something like this
bool success = false;
lock(lockForDictionary)
{
string val;
if (dict.TryGetValue(keyTryToRemove, out val) && val == valTryToRemove)
{
dict.Remove(keyTryToRemove);
success = true;
}
}
Below is a piece of code that simplify the steps a bit.
readonly object _locker = new object();
readonly ConcurrentDictionary<int, string> _dict = new ConcurrentDictionary<int, string>();
public bool TryRemove(int key, string value)
{
var success = false;
lock (_locker)
{
if (_dict.ContainsKey(key) && _dict[key] == value)
{
string val;
success = _dict.TryRemove(key, out val);
}
}
return success;
}
With that said, it seems the goal is non atomic in nature and this is why we have the need for a lock. It's important to ask, what is your goal and can you express the goal in an atomic way. 2 useful methods of ConcurrentDictionary include TryUpdate and AddOrUpdate. Would any of those methods help?
I have a C# class that acts as a dictionary so I'm now in the process of supporting IDictionary.
Everything is fine except for the properties Keys and Values:
ICollection<TKey> Keys { get; }
ICollection<TValue> Values { get; }
I don't have a collection of keys or values internally so I'm wondering how to provide these as a ICollection.
My first attempt was to use the magic of "yield return" like this:
ICollection<TValue> Values {
get {
for( int i = 0; i < nbValues; ++i ) {
yield return GetValue(i);
}
}
}
But of course this doesn't work since the returned type is not a IEnumerator but a ICollection...
It's too bad because this would have been the simplest solution !
My second attempt was to copy my values in a newly created array and return the array.
ICollection<TValue> Values {
get {
TValue[] copy = new TValue[nbValues];
for( int i = 0; i < nbValues; ++i ) {
copy[i] = GetValue(i);
}
return copy;
}
}
This would work since Array supports ICollection.
But the problem is that ICollection has methods to add and remove entries.
If the caller calls these methods only the copy will be modified not the dictionary...
The final solution I chose is to have my dictionary supports IDictionary but also ICollection and ICollection just so that I can return these collections from the properties Keys and Values...
public class MyDictionary : IDictionary<TKey,TValue>,
ICollection<TKey>,
ICollection<TValue>
{
}
So now the get accessor for the properties Keys and Values simply returns "this" ie: the dictionary.
ICollection<TValue> Values {
get {
return this;
}
}
It's probably the most optimal solution but I found it cumbersome to have to implement two extra interfaces whenever you want to implement IDictionary.
Do you have any other idea ?
I'm thinking that maybe returning the copy as an array was not such a bad idea after all. Anyway there is already a Add and Remove method in IDictionary which make more sense to be used.
Maybe returning a ReadOnlyCollection wrapping the array would be better as any attempt to modify the returned collection would fail?
ICollection<TValue> Values {
get {
TValue[] copy = new TValue[nbValues];
for( int i = 0; i < nbValues; ++i ) {
copy[i] = GetValue(i);
}
return new System.Collections.ObjectModel.ReadOnlyCollection<TValue>(copy);
}
}
I wouldn't personally expect you to be able to remove keys and values from a dictionary via Keys and Values anyway - I think it's fine to not do so.
Returning a ReadOnlyCollection<T> is fine - that waythe caller will just get an exception if they try to modify the collection, rather than the attempt just being silently ignored.
That exception follows the behaviour of Dictionary<TKey, TValue> by the way:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
IDictionary<string, string> dictionary =
new Dictionary<string, string> {{ "a", "b" }};
dictionary.Keys.Clear();
Console.WriteLine(dictionary.Count);
}
}
Results:
Unhandled Exception: System.NotSupportedException: Mutating a key collection
derived from a dictionary is not allowed.
at System.Collections.Generic.Dictionary`2
.KeyCollection.System.Collections.Generic.ICollection<TKey>.Clear()
at Test.Main()
As SLaks says, if you can create your own implementation of ICollection<T> which is lazy, that would be better - but if that's tricky for some reason, or indeed if the performance isn't important in your case, just creating the array and wrapping it in ReadOnlyCollection<T> is fine. You should consider documenting the expected performance either way though.
One thing to note if you do create your own lazy implementation: you should probably have some sort of "version number" to make sure that you invalidate the returned collection if the underlying data is changed.
A ReadOnlyCollection is the best approach of the options you listed; these collections are not supposed to be writable.
However that your getter is O(n), which is not good.
The correct approach is to create your own collection classes that implement ICollection<T> and return a live view of the dictionary. (and throw exceptions from mutation methods)
This is the approach taken by Dictionary<TKey, TValue>; it ensures that the property getters are fast, and does not waste extra memory.
Thank you all for your answers.
So I ended up doing two utility classes that implement the two ICollection requested by the Keys and Values properties. I have a few dictionaries where I need to add support for IDictionary so I will be reusing them a few times:
Here is the class for the collection of keys:
public class ReadOnlyKeyCollectionFromDictionary< TDictionary, TKey, TValue >
: ICollection<TKey>
where TDictionary : IDictionary<TKey,TValue>, IEnumerable<TKey>
{
IDictionary<TKey, TValue> dictionary;
public ReadOnlyKeyCollectionFromDictionary(TDictionary inDictionary)
{
dictionary = inDictionary;
}
public bool IsReadOnly {
get { return true; }
}
Here I implement ICollection<TKey> by simply calling the corresponding method on
the member "dictionary" but I throw a NotSupportedException for the methods Add,
Remove and Clear
public IEnumerator<TKey> GetEnumerator()
{
return (dictionary as IEnumerable<TKey>).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (dictionary as IEnumerable).GetEnumerator();
}
}
Here is the class for the collection of values:
public class ReadOnlyValueCollectionFromDictionary<TDictionary, TKey, TValue>
: ICollection<TValue>
where TDictionary : IDictionary<TKey, TValue>, IEnumerable<TValue>
{
IDictionary<TKey, TValue> dictionary;
public ReadOnlyValueCollectionFromDictionary(TDictionary inDictionary)
{
dictionary = inDictionary;
}
public bool IsReadOnly {
get { return true; }
}
Here I implement ICollection<TValue> by simply calling the corresponding method on
the member "dictionary" but I throw a NotSupportedException for the methods Add,
Remove and Clear
// I tried to support this one but I cannot compare a TValue with another TValue
// by using == since the compiler doesn't know if TValue is a struct or a class etc
// So either I add a generic constraint to only support classes (or ?) or I simply
// don't support this method since it's ackward in a dictionary anyway to search by
// value. Users can still do it themselves if they insist.
bool IEnumerable<TValue>.Contains(TValue value)
{
throw new System.NotSupportedException("A dictionary is not well suited to search by values");
}
public IEnumerator<TValue> GetEnumerator()
{
return (dictionary as IEnumerable<TValue>).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (dictionary as IEnumerable).GetEnumerator();
}
}
Then if my dictionary supports the IEnumerable for TKey and TValue everything becomes so simple:
public class MyDictionary : IDictionary<SomeKey,SomeValue>,
IEnumerable<SomeKey>,
IEnumerable<SomeValue>
{
IEnumerator<SomeKey> IEnumerable<SomeKey>.GetEnumerator()
{
for ( int i = 0; i < nbElements; ++i )
{
yield return GetKeyAt(i);
}
}
IEnumerator<SomeValue> IEnumerable<SomeValue>.GetEnumerator()
{
for ( int i = 0; i < nbElements; ++i )
{
yield return GetValueAt(i);
}
}
// IEnumerator IEnumerable.GetEnumerator() is already implemented in the dictionary
public ICollection<SomeKey> Keys
{
get
{
return new ReadOnlyKeyCollectionFromDictionary< MyDictionary, SomeKey, SomeValue>(this);
}
}
public ICollection<Value> Values
{
get
{
return new ReadOnlyValueCollectionFromDictionary< MyDictionary, SomeKey, SomeValue >(this);
}
}
}
It's too bad IDictionary is not returning IEnumerable instead of ICollection for the properties Keys and Values. All this would have been so much easier !
I'd like to test if an id was not yet known or, if it is known, if the associated value has changed. I'm currently using code similar to this, but it is hard to understand for those not familiar with the pattern. Can you think of a way to make it more readable while keeping it short in LOC?
string id;
string actual;
string stored;
if (!someDictionary.TryGetValue (id, out stored) || stored != actual) {
// id not known yet or associated value changed.
}
You can write an extension method with a good name:
public static class Utility
{
public static bool ValueChangedOrUnknown(this Dictionary<string, string> dictionary, string id, string actual)
{
string stored = null;
return (!dictionary.TryGetValue(id, out actual) || stored != actual);
}
}
so later you can use
string id;
string actual;
if (someDictionary.ValueChangedOrUnknown(id, actual) {
// id not known yet or associated value changed.
}
So I would most probably break it up and give it meaningful names. This is more to read, but you don't need much to say in comments:
bool isKnown = someDictionary.TryGetValue (id, out stored);
// can only change when it is known
bool valueChanged = isKnown && stored != actual;
// quite self-explanatory, isn't it?
if (!isKnown || valueChanged)
{
}
wrap each part of the || into its own method or property, than you can write it like this
if ( IdIsNew() || IdChanged())
Duality.
if (!(someDictionary.TryGetValue (id, out stored) && stored == actual)) ...
Not sure if it is more readable though... but it's good to know.
It looks fine to me...reads as easy as any other 2 condition if statement. About the only thing I'd possibly change is to flip the negations for an early exit:
if (someDictionary.TryGetValue(id, out stored) && stored == actual) {
return;
}
// store new value
I don't see any confusion in it at all, have never thought of it as a particularly troublesome idiom, and humbly suggest that those C# devs confused by it get used to it. It's common, succint, and gives as many LOC to the problem as it deserves. Turning it into 10 lines of code makes it way too important.
If I used it often, an extension method named something like ContainsEqualValue would be appropriate - but I'd use the exact same code in the extension method as you have.
I'd prefer a new method:
public bool ShouldSetValue(Dictionary someDictionary, object id,object actualValue)
{
string stored;
if (someDictionary.TryGetValue (id, out stored))
{
if (stored != actualValue)
return true;
}
else
{
return true;
}
}
then in the existing method I'd just:
if (ShouldSetValue(someDictionary,id,actual))
{
someDictionary[id]=actual;
}
An extension method would be slick:
public static class DictionaryExtensions
{
public static bool ShouldAddValue<TKey, TValue>(this Dictionary<TKey, TValue> someDictionary, TKey id, TValue actual)
{
TValue stored;
return (!someDictionary.TryGetValue(id, out stored) || !stored.Equals(actual));
}
}
Usage:
someDictionary.ShouldAddValue("foo", "bar")
If you mean that you have to do this repeatedly, and it is long and ugly, abstract the logic to another class and use an extension method.
public static class DictionaryExtensions
{
public static DictionaryChecker<TKey,TValue> contains<TKey,TValue>(this IDictionary<TKey,TValue> dictionary, TValue value)
{
return new DictionaryChecker<TKey,TValue>(value, dictionary);
}
}
public class DictionaryChecker<TKey,TValue>
{
TValue value;
IDictionary<TKey,TValue> dictionary;
internal DictionaryChecker(TValue value, IDictionary<TKey, TValue> dictionary)
{
this.value = value;
this.dictionary = dictionary;
}
public bool For(TKey key)
{
TValue result;
return dictionary.TryGetValue(key, out result) && result.Equals(value);
}
}
Now replace your code with:
if(!someDictionary.contains(actual).For(id)){
// id not known yet or associated value changed.
}
public T GetValue(int id, object actual)
{
object stored;
if (someDictionary.TryGetValue (id, out stored) || stored == actual)
return stored;
return new object();
}
While I recognize that the "try" pattern is necessary, I dislike implementations which require an "out" parameter. It would seem much more useful have functions similar to TryGetValue:
TryGetDictValue(dictionary, key) returns null if key is not in dictionary
TryGetDictValue(dictionary, key, defaultValue) returns defaultValue if key is not in dictionary
TryGetDictValue(dictionary, key, valueReturningDelegate) invokes the supplied delegate if key is not in dictionary and returns its result
In every case, the return type of the result would be that of the dictionary's data.
It's too bad there's no way to sneak into a time machine and make such things be methods of Dictionary. On the other hand, one could implement them as static functions taking a dictionary as the first parameter.
All I want is a dictionary which tells me which key it couldn't find, rather than just saying The given key was not present in the dictionary.
I briefly considered doing a subclass with override new this[TKey key], but felt it was a bit hacky, so I've gone with implementing the IDictionary interface, and passing everything through directly to an inner Dictionary, with the only additional logic being in the indexer:
public TValue this[TKey key]
{
get
{
ThrowIfKeyNotFound(key);
return _dic[key];
}
set
{
ThrowIfKeyNotFound(key);
_dic[key] = value;
}
}
private void ThrowIfKeyNotFound(TKey key)
{
if(!_dic.ContainsKey(key))
throw new ArgumentOutOfRangeException("Can't find key [" + key + "] in dictionary");
}
Is this the right/only way to go? Would newing over the this[] really be that bad?
Sounds like a good fit for an extension method:
public static class SomeUtilClass {
public static TValue VerboseGetValue<TKey, TValue>(
this IDictionary<TKey, TValue> data, TKey key)
{
TValue result;
if (!data.TryGetValue(key, out result)) {
throw new KeyNotFoundException(
"Key not found: " + Convert.ToString(key));
}
return result;
}
}
This will then work on all your existing dictionaries whenever you call VerboseGetValue, for example:
var data = new Dictionary<int, string> { { 123, "abc" } };
Console.WriteLine(data.VerboseGetValue(123));
Console.WriteLine(data.VerboseGetValue(456));
Instead of doing ContainsKey and checking for the presence of the key before touching the underlying dictionary, why not do
get {
try {
return _dic[key];
}
catch (ArgumentOutOfRangeException) {
throw new ArgumentOutOfRangeException(......);
}
}
That way, you only pay for the extra checking in the failure case - the success case, which is hopefully more common, doesn't have to do an extra dictionary lookup. This is good for get, but set is more difficult since the default behaviour of set is to always work. If you don't want that then you would need to check for the existence of the key first.
If you want to do this, you are going to have to roll your own in one way or another. But I'm going to question WHY you would want to do this?