Exploring CollectionsMarshal for Dictionary
Unlike ConcurrentDictionary
, Dictionary
does not have a GetOrAdd
method. This method is useful when you want to add a key-value pair to the dictionary if the key does not exist, or return the value if the key already exists. The naive implementation of this method looks like this:
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
where TKey : notnull
{
if (dict.TryGetValue(key, out var result))
return result;
dict[key] = value;
return value;
}
The issue here is that the dictionary is accessed twice: once to check if the key exists and once to add the key-value pair. This means you will compute the hash code of the key twice and call the Equals
method at least twice, which is not efficient.
A better way to do it is to use the CollectionsMarshal.GetValueRefOrAddDefault
method. The method returns a reference to the value associated with the key, and a boolean value indicating whether the key already exists. As the method returns a reference, you can update the value. Here is how you can use it:
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
where TKey : notnull
{
ref var dictionaryValue = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out var exists);
if (exists)
return dictionaryValue;
dictionaryValue = value;
return value;
}
CollectionMarshal
contains another useful method GetValueRefOrNullRef
that allows, for instance, to update the value associated with a key if if does exists:
public static bool TryUpdate<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
where TKey : notnull
{
ref var dictionaryValue = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key);
if (!Unsafe.IsNullRef(ref dictionaryValue))
{
dictionaryValue = value;
return true;
}
return false;
}
Do you have a question or a suggestion about this post? Contact me!