Почему ConcurrentBag <T>не реализует ICollection <T>? - PullRequest
27 голосов
/ 10 апреля 2011

У меня есть метод, который берет IList <> и добавляет к нему вещи.Я хотел бы передать ему ConcurrentBag в некоторых случаях, но он не реализует IList <> или ICollection <>, только неуниверсальный ICollection , который не имеетметод Add.

Теперь я понимаю, почему он не может (возможно) реализовать IList - это не упорядоченная коллекция, поэтому не имеет смысла иметь индексатор.Но я не вижу проблем ни с одним из методов ICollection <> .

Итак, почему?И, кроме того, есть ли в .NET потокобезопасная коллекция, которая реализует более надежные интерфейсы?

Ответы [ 4 ]

32 голосов
/ 19 апреля 2011

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");
    }
}
11 голосов
/ 19 апреля 2011

Это не значит, что 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> способом, должно быть довольно просто написать класс-оболочку (который использует шаблон адаптера ) для имитации методовICollection<T> с использованием ближайших доступных методов на ConcurrentBag<T>.

5 голосов
/ 10 апреля 2011

Есть SynchronizedCollection<T>, реализует как IList<T> и ICollection<T>, так и IEnumerable<T>.

0 голосов
/ 13 марта 2018

...

using System.Linq;


bool result = MyConcurrentBag.Contains("Item");

Предоставление возможности ICollection.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...