10

I'm tracing legacy code in my project written in C#.

I find the following code:

public class FooCollection : Collection<IFoo> {};

I don't understand why (and when) we need to create our own Collection class like this.

Why not just use the build-in collection (array, List<T>, Dictionary<K,V>, ...)

What's the data structure used in Collection<T> ?? array? or linked list?

Rowland Shaw
  • 37,700
  • 14
  • 97
  • 166
Hemingway Lee
  • 759
  • 1
  • 9
  • 21
  • 2
    By subclassing a generic, it is possible to extend its functionality beyond what the vanilla collection gives you. Can you possibly update the title to convey the `inherit from Collection` requirement? – StuartLC Dec 31 '13 at 12:41
  • If the derived class adds no new methods, then indeed this is just dead code. – Thomas Weller Dec 31 '13 at 12:46
  • 1
    Perhaps this will help you to figure out what the original developer was thinking about. http://stackoverflow.com/questions/398903/what-is-the-difference-between-list-of-t-and-collectionof-t – Tony Hopkinson Dec 31 '13 at 12:49
  • possible duplicate of [Collection class and it's use](http://stackoverflow.com/questions/12069987/collectiont-class-and-its-use) – nawfal Jul 09 '14 at 08:32

4 Answers4

7

I don't understand why (and when) we need to create our own Collection class like this.

You don't really "need" to; you could just use Collection<IFoo> directly, but having a specific class can help readability.

Also, it allows you to add specific behavior for this collection type as the Collection<T> class allows most operations to be redefined by overriding virtual methods; this allows you to customize the behavior of the standard collection methods.

So, for example, while you cannot directly override the Add method, you can override InsertItem which is then used by Add, AddRange etc.

Why not just use the build-in collection (array, List, Dictionary, ...)

An array has a fixed length, so you can't add or remove items; so it's not really equivalent. Dictionary<K,V> associates a value with a key, so it has a clearly different purpose. List<T> could be used instead, as long as you don't need to customize the behavior; this class is not designed for inheritance, since its methods are non-virtual.

What's the data structure used in Collection ?? array? or linked list?

Collection<T> acts as a wrapper around another IList<T>. By default, it uses a List<T> (which is based on an array), but you can pass any other IList<T> implementation to the constructor, and it will use that instead.

tomRedox
  • 28,092
  • 24
  • 117
  • 154
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • "Also, most methods of the Collection class are virtual" O really. You should read up on it again. Almost none of its methods are virtual. – Servy Dec 31 '13 at 15:40
  • @Servy They are. For example, `Add` internally calls the protected `InsertItem` method that is virtual. Same for clear, remove...etc. – ken2k Dec 31 '13 at 15:52
  • @ken2k There are exactly four virtual methods (beyond those of `object`), as I have described in my answer. That is most certainly not "most" of the methods. The *vast* majority are not virtual, and in my experiences, the very few methods that are virtual are insufficient for any meaningful extensions. – Servy Dec 31 '13 at 15:54
  • @Servy What `Add` does in only a call to `InsertItem`. Same for `Clear`, `Insert`, `Remove`, and `RemoveAt` for the other virtual methods. By overriding `InsertItem`, you can totally change the logic for `Add` too. – ken2k Dec 31 '13 at 15:59
  • @ken2k Yes, I'm well aware of that, however it is still *very* limiting in how you can meaningfully change the functionality of the collection. Creating a new collection is something to be done very rarely, and when done, it is generally to provide significant changes to core functionalities of a collection, not to provide subtle changes to only certain aspects of the functionality. – Servy Dec 31 '13 at 16:01
  • 1
    @Servy, you're right, my sentence was misleading. What I meant is that most *operations* (not methods) can be overriden (Add/Set/Remove/Clear). I'll update my answer. – Thomas Levesque Dec 31 '13 at 16:38
4

By stating that you are inheriting the Collection<T> class, you declare that your class IS-A Collection<T>, meaning that you have all it's API implemented (either by the derived class or by base the Collection<T> class).

The advantage in the inheritance is that you can decide to override some of the methods and handle them in a way you find more suitable to your needs or to the type T (IFoo in your case).

In the same manner you can also decide to extend your API in order to support some other functionality that you find appropriate for your situation.

For example if you class IFoo looks some thing like this:

public interface IFoo
{
   int a;
   int b;
}

In your derived class you can add an overload to the Remove that will look something like:

public bool Remove(int a, int b )...

And it will remove all occurrences of items that has certain values for a and b

Avi Turner
  • 10,234
  • 7
  • 48
  • 75
1

I don't understand why (and when) we need to create our own Collection class like this.

I never would. I personally find the Collection class to not be useful at all. I have not yet found a use for it.

One of the main problems with the type is that most of the method aren't virtual. The only virtual methods that are ClearItems, InsertItem, RemoveItem, and SetItem (along with Equals, GetHashCode, and ToString from Object). The rest of the methods/properties aren't virtual, and so cannot be overridden. This means you can alter the semantics for adding/inserting/removing an item slightly, but that's all.

What's the data structure used in Collection<T>?

Internally it is backed by a List<T>.

Why not just use the build-in collection (array, List<T>, Dictionary<K,V>, ...)

In virtually all instances you would. While there are occasional instances in which you might actually want to create a brand new collection type, I've never found a use in which Collection<T> was helpful in creating such a custom collection. Generally it's just best to implement some of the interfaces in the Collections namespace, based on what your collection can fulfill, and optionally compose the type using other collections as instance fields, if it is merely an extension of an existing collection.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • "This means you can alter the semantics for adding/inserting/removing an item slightly, but that's all" : and what else do you commonly do with collections? – Thomas Levesque Dec 31 '13 at 16:41
  • Also, by default it's backed by a List, but you can actually pass any other IList implementation to the constructor. – Thomas Levesque Dec 31 '13 at 16:42
  • @ThomasLevesque You change how the underlying data is accessed; affect views of that data rather than altering the underlying data itself, for example. Or you make some more significant change in how the underlying data is represented, to which this doesn't help you with. – Servy Dec 31 '13 at 16:43
  • yes, that's something you can do. You can't do everything with Collection, but it still covers many scenarios. If you want more customization than what Collection allows, you can still implement IList directly, but there are many cases where inheriting Collection is sufficient. In fact, many classes in the .NET framework inherit from it. – Thomas Levesque Dec 31 '13 at 16:47
  • @ThomasLevesque There are not many cases in which programmers should be defining their own collections in the first place. If this is something you're using *often* you should probably be using more composition instead of inheritance. Creating a new *collection* should be very rare. It will be somewhat less rare for a base language library, granted. If the changes being made to the collection are small and subtle, it's generally a pretty strong sign that you shouldn't be using inheritance in the first place. – Servy Dec 31 '13 at 16:52
1

I don't understand why (and when) we need to create our own Collection class like this.

One of the main use cases for implementing a custom Collection<T> is serialization.

If you want to customize how the collection is serialized (for example to XML, using XmlSerializable), you can create a custom collection class and implement IXmlSerializable on it. Then, you can change how to read or write the collection by manipulating the Read and Write methods of that interface.

class MyCollection : Collection<string>, IXmlSerializable
{
    public XmlSchema GetSchema()
    {
        return(null);
    }

    public void WriteXml(XmlWriter writer)
    {
        //// foreach (var item in Items)
            //// writer.Write...
    }

    public void ReadXml(XmlReader reader)
    {
        //// Items.Add(reader.Read...
    }
}

This is in most cases preferable compared to implementing IXmlSerializable on the parent class, as it leads to less code overall and promotes reuse (you can reuse the same collection in multiple places and it will be serialized in the same way as long as you use a XmlSerializer to serialize it).

I've used this recently due to a requirement from an external system that needed each element of the collection to generate a XML node with the index of the element appended to it.

The final structure looked somewhat like this:

<MyCollection>
  <Item1>first item</Item1>
  <Item2>second item</Item2>
  ...
</MyCollection>

I'm not fond of coupling the collection to the serialization like this, but sometimes it is a valid approach.

julealgon
  • 7,072
  • 3
  • 32
  • 77