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
What methods and properties you expose is a design decision. What base class you inherit from is an implementation detail. I feel it's worth taking a step back to the former.
An object is a collection of data and behaviour.
So your first questions should be:
Bear in mind that inheritance implies an "isa" (is a) relationship, whereas composition implies a "has a" (hasa) relationship. Choose the right one for your situation in your view, bearing in mind where things might go as your application evolves.
Consider thinking in interfaces before you think in concrete types, as some people find it easier to put their brain in "design mode" that way.
This isn't something everyone does consciously at this level in day to day coding. But if you're mulling this sort of topic, you're treading in design waters. Being aware of it can be liberating.
Take a look at List
Does footballTeam.Reverse() make sense to you? Does footballTeam.ConvertAll
This isn't a trick question; the answer might genuinely be "yes". If you implement/inherit List
If you decide yes, that makes sense, and you want your object to be treatable as a collection/list of players (behaviour), and you therefore want to implement ICollection or IList, by all means do so. Notionally:
class FootballTeam : ... ICollection
{
...
}
If you want your object to contain a collection/list of players (data), and you therefore want the collection or list to be a property or member, by all means do so. Notionally:
class FootballTeam ...
{
public ICollection Players { get { ... } }
}
You might feel that you want people to be able to only enumerate the set of players, rather than count them, add to them or remove them. IEnumerable
You might feel that none of these interfaces are useful in your model at all. This is less likely (IEnumerable
Anyone who attempts to tell you that one of these it is categorically and definitively wrong in every case is misguided. Anyone who attempts to tell you it is categorically and definitively right in every case is misguided.
Once you've decided on data and behaviour, you can make a decision about implementation. This includes which concrete classes you depend on via inheritance or composition.
This may not be a big step, and people often conflate design and implementation since it's quite possible to run through it all in your head in a second or two and start typing away.
An artificial example: as others have mentioned, a team is not always "just" a collection of players. Do you maintain a collection of match scores for the team? Is the team interchangable with the club, in your model? If so, and if your team isa collection of players, perhaps it also isa collection of staff and/or a collection of scores. Then you end up with:
class FootballTeam : ... ICollection,
ICollection,
ICollection
{
....
}
Design notwithstanding, at this point in C# you won't be able to implement all of these by inheriting from List
The guideline about not inheriting from collection classes isn't C# specific, you'll find it in many programming languages. It is received wisdom not a law. One reason is that in practice composition is considered to often win out over inheritance in terms of comprehensibility, implementability and maintainability. It's more common with real world / domain objects to find useful and consistent "hasa" relationships than useful and consistent "isa" relationships unless you're deep in the abstract, most especially as time passes and the precise data and behaviour of objects in code changes. This shouldn't cause you to always rule out inheriting from collection classes; but it may be suggestive.