7

In the .NET Framework, there is Dictionary and ConcurrentDictionary. These provide method like Add, Remove, and so on...

I know when we design a multi-thread program, we use ConcurrentDictionary to replace Dictionary for thread-safety.

I wonder why ConcurrentDictionary has AddOrUpdate, GetOrAdd and similar methods, while Dictionary has not.

We always like below code to get object from a Dictionary:

var dict = new Dictionary<string, object>();
object tmp;
if (dict.ContainsKey("key"))
{
       tmp = dict["key"];
}
else
{
       dict["key"] = new object();
       tmp = new object();
}

but when using ConcurrentDictionary, similar code is just one line only.

var conDict = new ConcurrentDictionary<string, object>();
var tmp = conDict.GetOrAdd("key", new object());

I expect .NET to have those methods, but why it doesn't?

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Po-Sen Huang
  • 445
  • 6
  • 24

1 Answers1

7

Because such methods are:

  1. A bare minimum for working in a concurrent context. You can't split the Get and Add in two separate steps without locking, and still yield correct results.

  2. When implemented for Dictionary<TKey, TValue>, it implicitly indicates some level of thread-safety, as if Dictionary<TKey, TValue> can handle this in a correct matter. It can't, so it is just not implemented. That doesn't stop you for making an extension method to do something similar.

     public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueGenerator)
     {
         //
         // WARNING: this method is not thread-safe and not intended as such.
         //
         if (!dict.TryGetValue(key, out TValue value))
         {
             value = valueGenerator(key);
    
             dict.Add(key, value);
         }
    
         return value;
     }
    
haindl
  • 3,111
  • 2
  • 25
  • 31
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • 2
    Actually, for a dictionary it might actually make sense to have the method in there, not from a thread safety perspective but from an optimization perspective. The dictionary has already worked out which bucket it expected the item to be in, which it was not, and then it can simply put the item into that bucket directly. With tryget+add it has to hash the key twice. – Lasse V. Karlsen Aug 23 '19 at 07:25
  • 1
    It does from that point of view, but in that regard, it is 'merely' a coded performance optimization. And I feel .NET can use some serious work on that. @LasseVågsætherKarlsen – Patrick Hofman Aug 23 '19 at 07:27
  • Also, when `Dictionary` was introduced, there was no such thing as lambdas. It would probably be less efficient when you'd always have to pass on the created instance, rather than a `Func<>`. – Patrick Hofman Aug 23 '19 at 07:28
  • @LasseV.Karlsen For that reason it is easy to build a "faster" Dictionary class, if you really need it. I always wondered why the obvious members were left out. – Frank Hileman Aug 06 '20 at 17:50