为什么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> 。
