Search code examples
c#.netcollectionsconcurrencyconcurrentdictionary

.NET Concurrent Dictionary Exchange Value


Is there a data structure similar to ConcurrentDictionary<TKey,TValue> and Interlocked.Exchange<T>(...) that would allow you atomically to set a new value and retrieve the old value assigned to an arbitrary key? Doesn't seem possible with any of the AddOrUpdate(...) overloads.


Solution

  • This functionality is not included out of the box. An API proposal for a Transact method exists in the GitHub dotnet/runtime repository, that would offer this functionality, but currently hasn't created much traction. You could implement it manually though, using the rudimentary TryGetValue, TryAdd and TryUpdate methods:

    public static TValue AddOrUpdate<TKey, TValue>(
        this ConcurrentDictionary<TKey, TValue> source,
        TKey key,
        Func<TKey, TValue> addValueFactory,
        Func<TKey, TValue, TValue> updateValueFactory,
        out bool oldValueExists,
        out TValue oldValue) where TKey : notnull
    {
        ArgumentNullException.ThrowIfNull(source);
        ArgumentNullException.ThrowIfNull(addValueFactory);
        ArgumentNullException.ThrowIfNull(updateValueFactory);
        while (true)
        {
            if (source.TryGetValue(key, out oldValue))
            {
                TValue newValue = updateValueFactory(key, oldValue);
                if (source.TryUpdate(key, newValue, oldValue))
                {
                    oldValueExists = true;
                    return newValue;
                }
            }
            else
            {
                TValue newValue = addValueFactory(key);
                if (source.TryAdd(key, addValueFactory(key)))
                {
                    oldValueExists = false;
                    oldValue = default;
                    return newValue;
                }
            }
        }
    }
    

    The above implementation is a modified version of the existing AddOrUpdate implementation.