Why doesn't ConcurrentBag<T> implement ICollection<T>?

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?


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");
    }
}

It's not that ConcurrentBag<T> couldn't implement ICollection<T> ; you can probably imagine that Contains could be implemented using TryPeek , or Remove with TryTake .

The issue is that treating a ConcurrentBag<T> as an ICollection<T> (eg, by allowing an implicit conversion when passing a ConcurrentBag<T> to a method that only takes ICollection<T> ) would be unwise, because most consumers of ICollection<T> expect it to have dramatically different semantics from ConcurrentBag<T> .

Most methods that take an ICollection<T> as a parameter are likely to make assumptions (that are safe in a single-threaded scenario) such as " Add followed by Contains will always return true ", or "if Contains returns true , so will Remove ". However, in highly-multithreaded situations (which is where one is likely to be using ConcurrentBag<T> in the first place), these assumptions are highly unlikely to hold. This could expose bugs in code that was written with the assumption of using ICollection<T> in a single-threaded scenario.

If you really do need to expose ConcurrentBag<T> as ICollection<T> (and you know that the code you're passing it to is expecting it to work in a non- ICollection<T> way), it should be fairly simple to write a wrapper class (that uses the adapter pattern) to simulate the methods of ICollection<T> using the closest available methods on ConcurrentBag<T> .


SynchronizedCollection<T> ,实现IList<T>ICollection<T>以及IEnumerable<T>

链接地址: http://www.djcxy.com/p/64330.html

上一篇: 为什么ICollection <T>没有实现ICollection?

下一篇: 为什么ConcurrentBag <T>不实现ICollection <T>?