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