When planning out my programs, I often start with a chain of thought like so:
A football team is just a list of football players. Therefore, I should
Classes should avoid deriving from classes and instead implement the minimal interfaces necessary.
Deriving from classes breaks encapsulation:
Among other things this makes it harder to refactor your code.
Classes are an implementation detail that should be hidden from other parts of your code.
In short a System.List
is a specific implementation of an abstract data type, that may or may not be appropriate now and in the future.
Conceptually the fact that the System.List data type is called "list" is a bit of a red-herring. A System.List<T>
is a mutable ordered collection that supports amortized O(1) operations for adding, inserting, and removing elements, and O(1) operations for retrieving the number of elements or getting and setting element by index.
When designing a data structure, the simpler the interface is, the more flexible the code is. Just look at how powerful LINQ is for a demonstration of this.
When you think "list" you should start by saying to yourself, "I need to represent a collection of baseball players". So let's say you decide to model this with a class. What you should do first is decide what the minimal amount of interfaces that this class will need to expose.
Some questions that can help guide this process:
This way you will not be coupling other parts of the code to implementation details of your baseball players collection and will be free to change how it is implemented as long as you respect the interface.
By taking this approach you will find that code becomes easier to read, refactor, and reuse.
Implementing interfaces in a modern IDE should be easy. Right click and choose "Implement Interface". Then forward all of the implementations to a member class if you need to.
That said, if you find you are writing lots of boilerplate, it is potentially because you are exposing more functions than you should be. It is the same reason you shouldn't inherit from a class.
You can also design smaller interfaces that make sense for your application, and maybe just a couple of helper extension functions to map those interfaces to any others that you need. This is the approach I took in my own IArray
interface for the LinqArray library.
When they say List<T>
is "optimized" I think they want to mean that it doesn't have features like virtual methods which are bit more expensive. So the problem is that once you expose List<T>
in your public API, you loose ability to enforce business rules or customize its functionality later. But if you are using this inherited class as internal within your project (as opposed to potentially exposed to thousands of your customers/partners/other teams as API) then it may be OK if it saves your time and it is the functionality you want to duplicate. The advantage of inheriting from List<T>
is that you eliminate lot of dumb wrapper code that is just never going to be customized in foreseeable future. Also if you want your class to explicitly have exact same semantics as List<T>
for the life of your APIs then also it may be OK.
I often see lot of people doing tons of extra work just because of FxCop rule says so or someone's blog says it's a "bad" practice. Many times, this turns code in to design pattern palooza weirdness. As with lot of guideline, treat it as guideline that can have exceptions.
When is it acceptable?
To quote Eric Lippert:
When you're building a mechanism that extends the
List<T>
mechanism.
For example, you are tired of the absence of the AddRange
method in IList<T>
:
public interface IMoreConvenientListInterface<T> : IList<T>
{
void AddRange(IEnumerable<T> collection);
}
public class MoreConvenientList<T> : List<T>, IMoreConvenientListInterface<T> { }