为什么ConcurrentBag <T>不实现ICollection <T>?
我有一个方法需要IList <>并添加内容。 我想在某些情况下将它传递给一个ConcurrentBag,但它不实现IList <>或ICollection <>,只有非泛型ICollection没有Add方法。
现在,我明白了为什么它不能(可能)实现IList - 它不是一个有序的集合,所以它没有任何意义,因为它有一个索引器。 但是我没有看到任何ICollection <>方法的问题。
所以为什么? 而且,在.NET中是否有一个线程安全的集合,它实现了更强大的接口?
List<T>
不是并发的,所以它可以实现ICollection<T>
,它给你一对方法Contains
和Add
。 如果Contains
返回false
,则可以安全地调用Add
知道它会成功。
ConcurrentBag<T>
是并发的,因此无法实现ICollection<T>
因为在您调用Add
时,答案Contains
返回可能无效。 相反,它实现了IProducerConsumerCollection<T>
它提供了一个方法TryAdd
,做的工作都Contains
和Add
。
所以不幸的是,你希望对两个既是集合又不共享通用接口的东西进行操作。 有很多方法可以解决这个问题,但是当API与这些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");
}
}
并不是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
”,或者“如果Contains
返回true
,那么将会Remove
“。 但是,在高度多线程的情况下(首先可能会使用ConcurrentBag<T>
),这些假设不太可能成立。 这可能会暴露在假设在单线程场景中使用ICollection<T>
编写的代码中的错误。
如果你确实需要将ConcurrentBag<T>
公开为ICollection<T>
(并且你知道你传递给它的代码期望它以非ICollection<T>
方式工作),它应该相当简单编写一个包装类(使用适配器模式)来使用ConcurrentBag<T>
上最接近的可用方法来模拟ICollection<T>
的方法。
有SynchronizedCollection<T>
,实现IList<T>
和ICollection<T>
以及IEnumerable<T>
。