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!