I have spent a fair amount of time in the past writing business objects, and as soon as we'd made a "Widget" class we needed a "WidgetList" class. I got pretty practiced at throwing these lists out quickly since they were all the same. Depending on the particular project's design decision, we'd encapsulate or inherit from a weakly-typed list.
A large number of innovations in computer programming are finding new ways to adhere to the ideal of DRY: Don't Repeat Yourself. So now with generics, all you need is "List<Widget> myList = new List<Widget>();"
The problem is that these lists weren't actually all the same. They did all had Add() and Clear() methods, Count and Items[] properties, and so on. But the item class was created because it wasn't just like anything else existing, and this carried over to the list. For instance, if it was a list of integers you wanted the Sum, Min, Max and Mean of the contents. If it was an address list, there'd be a method to find the primary billing address in a list of addresses for a client, and so on. Good object-oriented style says that those methods should be attached to the object where they do their work, i.e. on the list.
There are ways to do this in generics. Here’s a generic subclass of a generic list:
public class MyList<T> : List<T>
{
…
}
And a specific subclass of a generic list:
public class IntList : List<int>
{
// sum all the ints in the list
int Sum()
{
int result = 0;
foreach (int item in this)
result += item;
return result;
}
}
The only drawbacks to this technique that I know of are that the attributes that are attached to List<T> are not automatically attached to its subclasses. List<T> is marked [Serializable], but MyList<T> isn’t, unless you do so manually. It’s easy to forget that it’s not a complete drop-in-replacement unless you add the same attributes as the base class. John Rayner has noted similar issues.
Here’s a more complex example, using both generic and specific subclasses:
public class BusinessObject
{
.. virtual methods for all business objects
}
public class BusinessObjectList<T>: List<T>
where T: BusinessObject
{
… methods on lists of business objects
}
public class Widget: BusinessObject
{
…
}
public class WidgetList: BusinessObjectList<Widget>
{
.. a list of widgets, with businessObject-specific and widget-specific methods
}
And finally an odd one, a class that participates in its own base class:
public class GenericBase<T>
{
.. protected methods on T
}
public class Widget2 : GenericBase<Widget2>
{
}
Which means that Widget2 has access to all the inherited generic methods, in form strongly typed for its own use. I have not yet worked out if using this technique is a good idea or not. I got this example from a book that I was looking through; but the title escapes me now.