Exploring CollectionsMarshal for Dictionary

 
 
  • Gérald Barré

Unlike ConcurrentDictionary, Dictionary has no GetOrAdd method. Such a method is useful when you want to add a key-value pair if the key does not exist, or return the existing value if it does. The naive implementation 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 is that the dictionary is accessed twice: once to check if the key exists and once to add the key-value pair. This means the hash code is computed twice and Equals is called at least twice, which is inefficient.

A better approach is to use CollectionsMarshal.GetValueRefOrAddDefault, which returns a reference to the value associated with the key and a boolean indicating whether the key already existed. Because it returns a reference, you can update the value in place:

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;
}

CollectionsMarshal also provides GetValueRefOrNullRef, which allows you to update a value only if the key already 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?