我有一个方法,它采用IList<>并添加东西。在某些情况下,我想传递ConcurrentBag,但它没有实现IList<>或ICollection<>,只有非通用的ICollection,它没有Add方法。
现在,我明白为什么它不能(可能)实现IList - 它不是一个有序的集合,所以它有一个索引器没有意义。但我没有看到任何ICollection<>方法的问题。
那么,为什么?而且,在.NET中是否存在一个线程安全的集合,它实现了更强大的接口?
答案 0 :(得分:32)
List<T>
不是并发的,因此它可以实现ICollection<T>
,它为您提供了一对方法Contains
和Add
。如果Contains
返回false
,您可以安全地致电Add
,知道它会成功。
ConcurrentBag<T>
是并发的,因此无法实现ICollection<T>
,因为在您调用Contains
时,回复Add
可能无效。相反,它实现了IProducerConsumerCollection<T>
,它提供了执行TryAdd
和Contains
工作的单一方法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
返回true
,Remove
“也是如此。但是,在高度多线程的情况下(一开始可能会使用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功能。