Exploring CollectionsMarshal for Dictionary

 
 
  • Gérald Barré

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:

C#
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:

C#
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:

C#
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!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub