I have a ConcurrentDictionary object that I would like to set to a Dictionary object.
Casting between them is not allowed. So how do I do it?
I have a ConcurrentDictionary object that I would like to set to a Dictionary object.
Casting between them is not allowed. So how do I do it?
The ConcurrentDictionary<K,V>
class implements the IDictionary<K,V>
interface, which should be enough for most requirements. But if you really need a concrete Dictionary<K,V>
...
var newDictionary = yourConcurrentDictionary.ToDictionary(kvp => kvp.Key,
kvp => kvp.Value,
yourConcurrentDictionary.Comparer);
// or...
// substitute your actual key and value types in place of TKey and TValue
var newDictionary = new Dictionary<TKey, TValue>(yourConcurrentDictionary, yourConcurrentDictionary.Comparer);
Why do you need to convert it to a Dictionary? ConcurrentDictionary<K, V>
implements the IDictionary<K, V>
interface, is that not enough?
If you really need a Dictionary<K, V>
, you can copy it using LINQ:
var myDictionary = myConcurrentDictionary.ToDictionary(entry => entry.Key,
entry => entry.Value);
Note that this makes a copy. You cannot just assign a ConcurrentDictionary to a Dictionary, since ConcurrentDictionary is not a subtype of Dictionary. That's the whole point of interfaces like IDictionary: You can abstract away the desired interface ("some kind of dictionary") from the concrete implementation (concurrent/non-concurrent hashmap).
I think i have found a way to do it.
ConcurrentDictionary<int, int> concDict= new ConcurrentDictionary<int, int>( );
Dictionary dict= new Dictionary<int, int>( concDict);
ConcurrentDictionary<int, string> cd = new ConcurrentDictionary<int, string>();
Dictionary<int,string> d = cd.ToDictionary(pair => pair.Key, pair => pair.Value);
If you only need to guarantee thread-safety, but not a moment-in-time snapshot, use concurrentDictionary.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
.
This is because .ToDictionary()
is simply the LINQ implementation that operates over IEnumerable. The ConcurrentDictionary
enumerator is thread-safe, but the values can change underneath you as the enumerator is enumerated.
If you need to guarantee a moment-in-time snapshot, use concurrentDictionary.ToArray().ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
.
.ToArray()
is implemented by the ConcurrentDictionary
itself, and uses an internal lock to guarantee a moment-in-time snapshot of the dictionary contents. You may then do whatever you like with the resultant array, such as creating a new dictionary from its values.
Converting a ConcurrentDictionary<K,V>
to a Dictionary<K,V>
has a couple of gotchas.
The two dictionaries should have the same Comparer
. Otherwise two keys
that are different according to the one comparer might be equal according to the other comparer, in which case attempting to insert the second key in the target dictionary will cause a run-time ArgumentException
with the message "An item with the same key has already been added."
Although in practice a ConcurrentDictionary<K,V>
emits unique keys when it is enumerated, this is not guaranteed by the documentation, and the Microsoft engineers have explicitly stated that the observed behavior is not intentional. So there is nothing that prevents them from changing the current implementation in a future .NET version, in a way that will allow the emission of duplicate keys during a single enumeration of a ConcurrentDictionary<K,V>
instance. This could cause again the appearance of run-time ArgumentException
s, if you use the Dictionary<K,V>
constructor that has a collection
or dictionary
parameter. If you want to be on the safe side, it's a good idea to be defensive, and insert the key-value pairs in the Dictionary<K,V>
with either the TryAdd
method or the set
indexer. The TryAdd
will preserve the first occurrence of the duplicate key, while the set
indexer will preserve the last.
/// <summary>
/// Creates a Dictionary from a ConcurrentDictionary.
/// </summary>
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
this ConcurrentDictionary<TKey, TValue> source, bool snapshot = false)
{
ArgumentNullException.ThrowIfNull(source);
if (snapshot)
return new Dictionary<TKey, TValue>(source.ToArray(), source.Comparer);
// Create a Dictionary without snapshot semantics.
Dictionary<TKey, TValue> result = new(source.Comparer);
foreach (var (key, value) in source)
result.TryAdd(key, value); // or result[key] = value;
return result;
}
© 2022 - 2024 — McMap. All rights reserved.
IEqualityComparer
that won't be preserved that way! Better:var newDict = dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, dict.Comparer);
– Selfmade