7

Does a cast from ConcurrentDictionary to IDictionary cut off the thread-safe implementation, since IDictionary doesn't have GetOrAdd and AddOrUpdate methods ?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Luciano
  • 2,695
  • 6
  • 38
  • 53
  • How do you mean "cut", precisely? Does it remove it from `ConcurrentDictionary`? No. Is it available from the `IDictionary` interface? No. – James Michael Hare Apr 11 '12 at 18:57
  • 2
    @JamesMichaelHare: He means that, if you cast to `IDictionary`, does it make the resulting object thread-unsafe? In other words, does it turn it into an ordinary dictionary? – Robert Harvey Apr 11 '12 at 18:59
  • Will it still be thread-safe? Yes insomuch as it can be without those operations. That is, if you need an atomic `GetOrAdd()` you'll be out of luck. – James Michael Hare Apr 11 '12 at 19:03
  • @JamesMichaelHare `GetOrAdd()` is not atomic. The delegate is executed outside the internal lock to prevent deadlocks. see http://msdn.microsoft.com/en-us/library/dd997369.aspx – Mike Zboray Apr 12 '12 at 07:20
  • @mikez: Yes, sorry, atomic was the wrong choice of words. My bad. I meant to say that the actual Add() if it doesn't exist would be synchronized so only one competitor actually does the Add(), but as you correctly point out from the MSDN multiple simultaneous callers may execute the delegate and end up generating an un-needed item which is ignored. – James Michael Hare Apr 12 '12 at 14:06

6 Answers6

11

The resulting object will still be a concurrent dictionary. The calls like Add or Remove use the underlying implementation TryAdd and TryRemove (which are thread-safe). Casting an object to a different type doesn't change the object itself.

Also, for clarification, you could use tools like ILSpy to see what's the implementation of default IDictionary methods and whether they'll be still thread-safe.

Dmitry Reznik
  • 6,812
  • 2
  • 32
  • 27
  • 1
    You can also look at the MSDN documentation for ConcurrentDictionary's implementations of the `IDictionary` methods: http://msdn.microsoft.com/en-us/library/dd287191.aspx – phoog Apr 11 '12 at 19:12
6

IDictionary is just an interface. If you cast to it, the result is an implementation of ConcurrentDictionary, missing the GetOrAdd and AddOrUpdate methods.

Presumably, you can still use the Item property and the Add and ContainsKey methods (in lieu of the GetOrAdd and AddOrUpdate) methods, and your casted object will still be thread-safe (since the underlying implementation is a ConcurrentDictionary).

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
1

The interface doesn't affect the implementation. It just doesn't exposed some of ConcurrentDictionary's methods.

You may find this or this helpful in understanding interfaces.

Community
  • 1
  • 1
WhiteKnight
  • 4,938
  • 5
  • 37
  • 41
1

It would be like looking at big ConcurrentDictionary object through IDictionary shaped keyhole - you could only see IDictionary shape but it would still be ConcurrentDictionary.

Maciej
  • 7,871
  • 1
  • 31
  • 36
0

When a ConcurrentDictionary<K,V> is exposed as an IDictionary<K,V>, it is thread-safe in a very limited sense: the consistence of its internal state is guaranteed. No undocumented exceptions will be thrown, and no values are going to emerge out of the blue that were never added to the dictionary. But in a broader sense of the term, taking into account how the dictionary interacts with the rest of the application and how meaningful are the data that it contains, it's unlikely to be thread-safe. The reason is that the IDictionary<K,V> interface doesn't expose the specialized atomic APIs that make the ConcurrentDictionary<K,V> suitable for multithreaded usage. Without these APIs, many common operations become impossible.

For example let's say that you have a ConcurrentDictionary<string, int>, and you want to increment the value of the key "A", or, if the key does not exist, add it with the value 1. The correct API to use is the AddOrUpdate:

dictionary.AddOrUpdate("A", 1, (_, existing) => existing + 1);

Without this API, you are stuck. By using only the members of the IDictionary<string, int> interface, the best you can do is this:

if (dictionary.TryGetValue("A", out int existing))
    dictionary["A"] = existing + 1;
else
    dictionary.Add("A", 1);

...which is irreparably flawed because the operation is not atomic. Another thread might change the value of the key, between the calls to the TryGetValue and to the set indexer. So the final value for the key "A" might not be equal to the number of times that it was (supposedly) incremented. There is no way to fix this race condition. The IDictionary<K,V> interface was simply not designed for multithreaded usage.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
-1

Short answer no.

You are manipulating an object through an interface and hence still using the concrete implementation. You are not losing any functionality nor its methods. They are just not available.

On the side note, you need an explicit cast when downcasting, no need for an explicit cast when upcasting - always safe to do so.

ra170
  • 3,643
  • 7
  • 38
  • 52
  • sorry but ConcurrentDictionary add two new methods and hide others, so its implementation could add thread-safe only for the new methods, and not for the hidden methods. MSDN documentation doesn't clarify it, so I asked it. But after I read the best comments and I took a look at ConcurrentDictionary prototype I saw what it doens't extends Dictionary, and this reassured me that is improbable that casting it to IDictionary would indirectly use the extended version Dictionary. – Luciano Apr 13 '12 at 21:16