dbarnes
dbarnes

Reputation: 1833

Hiding public members of an interface in IL

Consider these lines of code:

ConcurrentDictionary<string, object> error = new ConcurrentDictionary<string, object>();
error.Add("hello", "world"); //actually throws a compiler error

and

IDictionary<string, object> noError = new ConcurrentDictionary<string, object>();
noError.Add("hello", "world");

I eventually figure out that all you have to do is change the IL to make the Add function private.

Now in the spirit of decoupled code I'd most likely use the Interface but it seems that Concurrent dictionary isn't too found of the Add method.

Is it safe to really use Add(I can't view the IL so I don't know if it's really thread safe.)? Or should I use the concrete type of ConcurrentDictionary<TKey, TValue> and explicitly use TryAdd.

Upvotes: 4

Views: 178

Answers (2)

Hans Passant
Hans Passant

Reputation: 942255

Hmya, you are playing a dangerous game. The public interface of the ConcurrentDictionary class provides thread-safe methods that you can feel good about calling, knowing that they behave well.

But the Add() method is not such a method, only TryAdd() is. You get the compile error because Add() is not public, Microsoft intentionally made the method inaccessible by writing an explicit interface implementation version of it. Otherwise something they had to do, ConcurrentDictionary implements IDictionary. Whether they should have implemented that interface is debatable. But water over the bridge, they did.

And sure, you can very easily cast to access Add(). But now that feel good feeling is starting to develop frays around the edges. Quite appropriately, the only way they could implement Add() is to make it throw an exception when TryAdd() fails. Kaboom with an ArgumentException, good luck debugging that.

Are you absolutely 100 percent sure that you can deal with an exception on a worker thread when such a basic operation fails? Did you, instead of thinking about hacking IL, think about what you need to do in your catch clause? And if you did, exactly how it is that different from the code you write when TryAdd() returns false?

It is not different.

Upvotes: 5

The Vermilion Wizard
The Vermilion Wizard

Reputation: 5415

Yes, it's safe.

Have a look at the reference source for ConcurrentDictionary. The method IDictionary<TKey, TValue>.Add simply calls TryAdd and throws an exception if the key already exists.

The hiding of members of an interface is not something that requires IL modifications to be done. It can be done through explicit interface implementation in C#. This is done by leaving off the access modifier of the method and prefixing the method name with the interface name:

void IDictionary<TKey,TValue>.Add(TKey key, TValue value) {}

There are various reasons for doing this, maybe you don't want to clutter the concrete interface, or you want consumers of your class to be explicit about what method they are using if the name of the methods on the interface aren't specific enough. Also, it allows you to provide separate implementations for methods on different interfaces with the same signature (not really an issue for ConcurrentDictionary I think, but the capability is there if you need it in your own classes).

Upvotes: 8

Related Questions