Search code examples
c#inheritanceconcurrencyencapsulationconcurrent-collections

Inheriting/Encapsulating a concurrent collection c#


I'm creating a custom collection that encapsulates a ConcurrentDictionary. I found a lot of information on encapsulating/inheriting from a generic collection but nothing specific to concurrent collections. Here is a code snippet of my base case, followed by some general questions.

class ItemCollection
{
    private ConcurrentDictionary<string, Item> Collection;        

    public ItemCollection() { Collection = new ConcurrentDictionary<string, Item>(); }

    public bool TryAdd(string webId, string name) { return Collection.TryAdd(webId, new Item(name)); }
    public bool TryAdd(string webId, Item item) { return Collection.TryAdd(webId, item); }
    public bool TryUpdate(KeyValuePair<string, Item> entry, Data data) 
    {
        Item newItem = entry.Value;
        newItem.AddData(data);            
        return Collection.TryUpdate(entry.Key, newItem, entry.Value);
    }
}
  • Is encapsulating concurrent collections in this manner acceptable, or is this moving into the realm of creating your own thread-safe collection from a generic collection?
  • Is the custom collection thread-safe?
  • Is inheriting a concurrent collection ever acceptable? Like so class ItemCollection : ConcurrentDictionary<string, Item> and if so what are some guidelines, similar to this for inheriting from non-concurrent collections.
  • How do you implement forwarding methods for methods like Select? I tried a series of variations like the following but can't get it to work:

public IEnumerable<TResult> Select<ItemCollection, TResult>(this ItemCollection source, Func<KeyValuePair<string, Item>, TResult> selector) { return Collection.Select(selector); }

If I inherit the ConcurrentDictionary it results in an implementation like enter image description here


Solution

  • The question is bordering on "too broad". That said, some basic answers can be provided. For more detail, you will have to narrow your question(s) down further, dealing with smaller, more specific issues at a time, and preferably presenting those questions individually.

    In the meantime:

    Is encapsulating concurrent collections in this manner acceptable, or is this moving into the realm of creating your own thread-safe collection from a generic collection?

    "Acceptable" is a broad concept, but…yes, there's nothing fundamentally wrong with the approach.

    Is the custom collection thread-safe?

    "Thread safe" is a vague term. But in general, I'd say "yes". Your class doesn't maintain any of its own state at all, instead delegating all state to a class that is thread-safe. So for the usual concerns of thread-safety, the class state itself should be fine.

    Note that the TryUpdate() method does also modify the passed-in object, which is not done in a thread-safe way. But that's more about the thread safety of that object, not of your collection.

    Is inheriting a concurrent collection ever acceptable? Like so class ItemCollection : ConcurrentDictionary and if so what are some guidelines, similar to this for inheriting from non-concurrent collections.

    It's always "acceptable" to inherit a non-sealed class. Whether it's useful to do so is a different matter. Inheritance is most useful when the inherited class has useful protected and/or virtual members that the derived class can use to actually extend or otherwise modify the behavior of the inherited class.

    Otherwise, extension methods are often a better approach.

    In any case, I would not expect that guidance for inheritance of collections would vary much if at all depending on whether the collection is thread-safe or not.

    How do you implement forwarding methods for methods like Select? I tried a series of variations like the following but can't get it to work:

    "Methods like Select()" are implemented as extension methods, and rely on the object itself implementing IEnumerable<T> (for some such methods, IEnumerable is good enough). If you want your custom collection to be able to use those methods, you'll have to implement the appropriate interface for the extension method(s) you want to be used with your class.

    Naturally, in your implementation of an interface, you can delegate the actual operation to the encapsulated collection.