为什么ConcurrentBag <t>没有实现ICollection <t>?</t> </t>

时间:2011-04-10 12:42:32

标签: c# concurrency

我有一个方法,它采用IList&lt;&gt;并添加东西。在某些情况下,我想传递ConcurrentBag,但它没有实现IList&lt;&gt;或ICollection&lt;&gt;,只有非通用的ICollection,它没有Add方法。

现在,我明白为什么它不能(可能)实现IList - 它不是一个有序的集合,所以它有一个索引器没有意义。但我没有看到任何ICollection<>方法的问题。

那么,为什么?而且,在.NET中是否存在一个线程安全的集合,它实现了更强大的接口?

4 个答案:

答案 0 :(得分:32)

List<T>不是并发的,因此它可以实现ICollection<T>,它为您提供了一对方法ContainsAdd。如果Contains返回false,您可以安全地致电Add,知道它会成功。

ConcurrentBag<T>是并发的,因此无法实现ICollection<T>,因为在您调用Contains时,回复Add可能无效。相反,它实现了IProducerConsumerCollection<T>,它提供了执行TryAddContains工作的单一方法Add

所以不幸的是,您希望对两个集合进行操作,但这两个集合都不是共享接口。有很多方法可以解决这个问题,但是当API与这些问题类似时,我首选的方法是为两个接口提供方法重载,然后使用lambda表达式来创建使用自己的方法为每个接口执行相同操作的委托。然后,您可以使用该代理代替您执行几乎常见操作的位置。

这是一个简单的例子:

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

答案 1 :(得分:11)

ConcurrentBag<T> 不能实施ICollection<T>;您可以想象Contains可以使用TryPeek实现,Remove可以使用TryTake实现。

问题在于将ConcurrentBag<T>视为ICollection<T>(例如,在将ConcurrentBag<T>传递给只需ICollection<T>的方法时允许隐式转换)是不明智的,因为ICollection<T>的大多数消费者都希望它与ConcurrentBag<T>具有截然不同的语义。

ICollection<T>作为参数的大多数方法都可能做出假设(在单线程方案中是安全的),例如“Add后跟Contains将始终返回true“或”if Contains返回trueRemove“也是如此。但是,在高度多线程的情况下(一开始可能会使用ConcurrentBag<T>),这些假设极不可能成立。这可能会暴露代码中的错误,这些错误是在假设在单线程场景中使用ICollection<T>编写的。

如果您确实需要将ConcurrentBag<T>公开为ICollection<T>(并且您知道您传递给它的代码希望它以非ICollection<T>方式工作) ,使用ICollection<T>上最接近的可用方法编写包装类(使用adapter pattern)来模拟ConcurrentBag<T>的方法应该相当简单。

答案 2 :(得分:5)

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

答案 3 :(得分:0)

...

using System.Linq;


bool result = MyConcurrentBag.Contains("Item");

提供各种ICollection功能。