Ответ 1
A List<T>
не является параллельным и поэтому может реализовать ICollection<T>
, который дает вам пару методов Contains
и Add
. Если Contains
возвращает false
, вы можете безопасно позвонить Add
, зная, что он будет успешным.
A ConcurrentBag<T>
является параллельным и поэтому он не может реализовать ICollection<T>
, потому что ответ Contains
возвращает может быть недействительным к моменту вызова Add
. Вместо этого он реализует IProducerConsumerCollection<T>
, который предоставляет единственный метод TryAdd
, который выполняет работу как Contains
, так и Add
.
Так что, к сожалению, вы хотите работать над двумя вещами, которые являются коллекциями, но не имеют общего интерфейса. Есть много способов решить эту проблему, но мой предпочтительный подход, когда API так же схож, как и в том, чтобы обеспечить перегрузку методов для обоих интерфейсов, а затем использовать лямбда-выражения для создания делегатов, которые выполняют одну и ту же операцию для каждого интерфейса, используя свои собственные методы. Затем вы можете использовать этот делегат вместо того, где вы выполнили почти общую операцию.
Вот простой пример:
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");
}
}