I have a method which takes an IList<> and adds stuff to it. I would like to pass it a ConcurrentBag in some cases, but it doesn't implement IList<> or ICollection<>, only the non-generic ICollection, which doesn't have an Add method.
Now, I see why it can't (maybe) implement IList - it's not an ordered collection so it won't make sense for it to have an indexer. But I don't see an issue with any of the ICollection<> methods.
So, why? And, also - is there a thread-safe collection in .NET that does implement more robust interfaces?
ConcurrentBag allows you to store objects in unordered way. Contrary to ConcurrentDictionary class, it allows you to store duplicate objects. ConcurrentBag allows multiple threads to store the objects. It is optimized for scenarios where same thread act as producer and consumer.
ConcurrentBag<T> is a thread-safe bag implementation, optimized for scenarios where the same thread will be both producing and consuming data stored in the bag.
A List<T>
is not concurrent and so it can implement ICollection<T>
which gives you the pair of methods Contains
and Add
. If Contains
returns false
you can safely call Add
knowing it will succeed.
A ConcurrentBag<T>
is concurrent and so it cannot implement ICollection<T>
because the answer Contains
returns might be invalid by the time you call Add
. Instead it implements IProducerConsumerCollection<T>
which provides the single method TryAdd
that does the work of both Contains
and Add
.
So unfortunately you desire to operate on two things that are both collections but don't share a common interface. There are many ways to solve this problem but my preferred approach when the API is as similar as these are is to provide method overloads for both interfaces and then use lambda expressions to craft delegates that perform the same operation for each interface using their own methods. Then you can use that delegate in place of where you would have performed the almost common operation.
Here's a simple example:
public class Processor { /// <summary> /// Process a traditional collection. /// </summary> /// <param name="collection">The collection.</param> public void Process(ICollection<string> collection) { Process(item => { if (collection.Contains(item)) return false; collection.Add(item); return true; }); } /// <summary> /// Process a concurrent collection. /// </summary> /// <param name="collection">The collection.</param> public void Process(IProducerConsumerCollection<string> collection) { Process(item => collection.TryAdd(item)); } /// <summary> /// Common processing. /// </summary> /// <param name="addFunc">A func to add the item to a collection</param> private void Process(Func<string, bool> addFunc) { var item = "new item"; if (!addFunc(item)) throw new InvalidOperationException("duplicate item"); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With