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.
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.