By Superbest

2014-02-11 03:01:36 8 Comments

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 represent it with:

var football_team = new List<FootballPlayer>();

The ordering of this list represent the order in which the players are listed in the roster.

But I realize later that teams also have other properties, besides the mere list of players, that must be recorded. For example, the running total of scores this season, the current budget, the uniform colors, a string representing the name of the team, etc..

So then I think:

Okay, a football team is just like a list of players, but additionally, it has a name (a string) and a running total of scores (an int). .NET does not provide a class for storing football teams, so I will make my own class. The most similar and relevant existing structure is List<FootballPlayer>, so I will inherit from it:

class FootballTeam : List<FootballPlayer> 
    public string TeamName; 
    public int RunningTotal 

But it turns out that a guideline says you shouldn't inherit from List<T>. I'm thoroughly confused by this guideline in two respects.

Why not?

Apparently List is somehow optimized for performance. How so? What performance problems will I cause if I extend List? What exactly will break?

Another reason I've seen is that List is provided by Microsoft, and I have no control over it, so I cannot change it later, after exposing a "public API". But I struggle to understand this. What is a public API and why should I care? If my current project does not and is not likely to ever have this public API, can I safely ignore this guideline? If I do inherit from List and it turns out I need a public API, what difficulties will I have?

Why does it even matter? A list is a list. What could possibly change? What could I possibly want to change?

And lastly, if Microsoft did not want me to inherit from List, why didn't they make the class sealed?

What else am I supposed to use?

Apparently, for custom collections, Microsoft has provided a Collection class which should be extended instead of List. But this class is very bare, and does not have many useful things, such as AddRange, for instance. jvitor83's answer provides a performance rationale for that particular method, but how is a slow AddRange not better than no AddRange?

Inheriting from Collection is way more work than inheriting from List, and I see no benefit. Surely Microsoft wouldn't tell me to do extra work for no reason, so I can't help feeling like I am somehow misunderstanding something, and inheriting Collection is actually not the right solution for my problem.

I've seen suggestions such as implementing IList. Just no. This is dozens of lines of boilerplate code which gains me nothing.

Lastly, some suggest wrapping the List in something:

class FootballTeam 
    public List<FootballPlayer> Players; 

There are two problems with this:

  1. It makes my code needlessly verbose. I must now call my_team.Players.Count instead of just my_team.Count. Thankfully, with C# I can define indexers to make indexing transparent, and forward all the methods of the internal List... But that's a lot of code! What do I get for all that work?

  2. It just plain doesn't make any sense. A football team doesn't "have" a list of players. It is the list of players. You don't say "John McFootballer has joined SomeTeam's players". You say "John has joined SomeTeam". You don't add a letter to "a string's characters", you add a letter to a string. You don't add a book to a library's books, you add a book to a library.

I realize that what happens "under the hood" can be said to be "adding X to Y's internal list", but this seems like a very counter-intuitive way of thinking about the world.

My question (summarized)

What is the correct C# way of representing a data structure, which, "logically" (that is to say, "to the human mind") is just a list of things with a few bells and whistles?

Is inheriting from List<T> always unacceptable? When is it acceptable? Why/why not? What must a programmer consider, when deciding whether to inherit from List<T> or not?


@phoog 2020-05-21 19:48:57

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> { }

@Nean Der Thal 2014-02-11 15:45:15

class FootballTeam : List<FootballPlayer> 
    public string TeamName; 
    public int RunningTotal;

Previous code means: a bunch of guys from the street playing football, and they happen to have a name. Something like:

People playing football

Anyway, this code (from m-y's answer)

public class FootballTeam
    // A team's name
    public string TeamName; 

    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
        get { return _roster; }

    public int PlayerCount
        get { return _roster.Count(); }

    // Any additional members you want to expose/wrap.

Means: this is a football team which has management, players, admins, etc. Something like:

Image showing members (and other attributes) of the Manchester United team

This is how is your logic presented in pictures…

@Ben 2015-04-13 17:13:03

I would expect that your second example is a Football Club, not a football team. A football club has managers and admin etc. From this source: "A football team is the collective name given to a group of players ... Such teams could be selected to play in a match against an opposing team, to represent a football club ...". So it is my opinion that a team is a list of players (or perhaps more accurately a collection of players).

@SiD 2016-07-20 12:24:22

More optimised private readonly List<T> _roster = new List<T>(44);

@Davide Cannizzo 2017-12-12 15:18:40

It's necessary to import System.Linq namespace.

@F8ER 2020-03-25 15:39:23

For visuals: +1

@Eric Lippert 2014-02-11 05:43:29

There are some good answers here. I would add to them the following points.

What is the correct C# way of representing a data structure, which, "logically" (that is to say, "to the human mind") is just a list of things with a few bells and whistles?

Ask any ten non-computer-programmer people who are familiar with the existence of football to fill in the blank:

A football team is a particular kind of _____

Did anyone say "list of football players with a few bells and whistles", or did they all say "sports team" or "club" or "organization"? Your notion that a football team is a particular kind of list of players is in your human mind and your human mind alone.

List<T> is a mechanism. Football team is a business object -- that is, an object that represents some concept that is in the business domain of the program. Don't mix those! A football team is a kind of team; it has a roster, a roster is a list of players. A roster is not a particular kind of list of players. A roster is a list of players. So make a property called Roster that is a List<Player>. And make it ReadOnlyList<Player> while you're at it, unless you believe that everyone who knows about a football team gets to delete players from the roster.

Is inheriting from List<T> always unacceptable?

Unacceptable to who? Me? No.

When is it acceptable?

When you're building a mechanism that extends the List<T> mechanism.

What must a programmer consider, when deciding whether to inherit from List<T> or not?

Am I building a mechanism or a business object?

But that's a lot of code! What do I get for all that work?

You spent more time typing up your question that it would have taken you to write forwarding methods for the relevant members of List<T> fifty times over. You're clearly not afraid of verbosity, and we are talking about a very small amount of code here; this is a few minutes work.


I gave it some more thought and there is another reason to not model a football team as a list of players. In fact it might be a bad idea to model a football team as having a list of players too. The problem with a team as/having a list of players is that what you've got is a snapshot of the team at a moment in time. I don't know what your business case is for this class, but if I had a class that represented a football team I would want to ask it questions like "how many Seahawks players missed games due to injury between 2003 and 2013?" or "What Denver player who previously played for another team had the largest year-over-year increase in yards ran?" or "Did the Piggers go all the way this year?"

That is, a football team seems to me to be well modeled as a collection of historical facts such as when a player was recruited, injured, retired, etc. Obviously the current player roster is an important fact that should probably be front-and-center, but there may be other interesting things you want to do with this object that require a more historical perspective.

@Superbest 2014-02-11 06:19:30

Although the other answers have been very helpful, I think this one addresses my concerns most directly. As for exaggerating the amount of code, you are right that it isn't that much work in the end, but I do get confused very easily when I include some code in my program without understanding why I am including it.

@Eric Lippert 2014-02-11 06:25:47

@Superbest: Glad I could help! You are right to listen to those doubts; if you don't understand why you're writing some code, either figure it out, or write different code that you do understand.

@Jules 2014-02-11 08:22:24

@EricLippert "You spent more time typing up your question that it would have taken you to write forwarding methods for the relevant members of List<T> fifty times over." -- It's been a while since I worked in Visual Studio, but when working in Java with Eclipse the environment can autogenerate these methods (Source/Generate delegate methods/select the list/finish: 4 mouse clicks). Does VS not have an option to do this?

@peterh - Reinstate Monica 2014-02-11 08:41:48

The difference between the so-named "mechanism" and "business objects" doesn't explain, why a List<T> shouldn't be extended to a "business object". Every object are "mechanisms", although not always so clear and simple as a such generic element.

@user541686 2014-02-11 10:59:41

"When is it acceptable? When you're building a mechanism that extends the List<T> mechanism."... or when you must care about the performance of that extra level of indirection, which no one seems to ever mention.

@AJMansfield 2014-02-11 12:58:27

@Mehrdad But you seem to forget the golden rules of optimization: 1) don't do it, 2) don't do it yet, and 3) don't do it without first doing performance profiles to show what needs optimizing.

@Eric Lippert 2014-02-11 14:37:26

@Mehrdad: Honestly, if your application requires that you care about the performance burden of, say, virtual methods, then any modern language (C#, Java, etc) is not the language for you. I have a laptop right here that could run a simulation of every arcade game I played as a child simultaneously. This allows me to not worry about a level of indirection here and there.

@Eric Lippert 2014-02-11 14:38:36

@Jules: VS has such a mechanism for implementing interfaces; I don't know offhand if it has one for forwarding methods. It would be easy to write in Roslyn if it doesn't exist.

@Brian 2014-02-11 14:54:16

@Jules: I'm not confident enough in my VS skills to insist that the VS IDE lacks this feature, but will note that some VS extensions (e.g., R#) do support this.

@user541686 2014-02-11 16:45:38

@EricLippert: If you don't need to worry about it then you don't need to worry about it. If you do, then you do. All I was saying is that when it's a bottleneck, inheritance instead of composition can be a solution. No reason why you have to dump the entire language just because you came across a bottleneck somewhere...

@WernerCD 2014-02-11 17:52:58

Obviously the current player roster is an important fact that should probably be front-and-center, but there may be other interesting things you want to do with this object that require a more historical perspective. - Consider YAGNI? Sure you MIGHT at some point in the near or far future need to look at that stuff... but do you need to NOW? Building for the possibility of a future need (that may or may not ever exist...) is wasting effort.

@Eric Lippert 2014-02-11 18:00:43

@WernerCD: I agree absolutely. We are not given by the original poster what the business of the program is, just that football teams are involved. If this is a simulation for a game, that's one thing. If it's intended to model reality, that's another entirely.

@Superbest 2014-02-11 18:11:10

With regard to your update: I was actually trying to make a Delta-V calculator, which consumes a list of rocket parts (with properties such as mass and fuel amount). Indeed, a rocket does not have a single list of parts, but it has different sets of parts for each stage. I've handled it by giving the rocket a List<Stage>, where each Stage has a List<RocketPart> - although I became curious about the general problem aside from this particular program.

@Eric Lippert 2014-02-11 18:15:08

@Superbest: Now we are definitely at the "composition not inheritance" end of the spectrum. A rocket is composed of rocket parts. A rocket is not "a special kind of parts list"! I would suggest to you that you trim it down further; why a list, as opposed to an IEnumerable<T> -- a sequence?

@Matt Johnson-Pint 2014-02-11 22:29:21

@EricLippert - You should take a look at Fowler's Temporal Patterns write-up if you haven't already. :) But whether you need to do that or not is heavily dependent on business use case and storage/retrieval requirements. For example, you might want your object to represent only a snapshot, but have your database infrastructure be aware of the temporal aspect. I did exactly that in this project.

@Eric Lippert 2014-02-11 22:39:31

@MattJohnson: Thanks for the link. One of the ironies of how OOP is taught is that "Bank Account" is often an early exercise given to students. And of course how they are taught to model it is the exact opposite of how real bank accounts are modeled in banking software. In real software an account is an append-only list of debits and credits from which a balance may be computed, not a mutable balance that is destroyed and replaced when an update happens.

@Magus 2014-02-11 23:44:20

@Mehrdad: To reiterate what Eric Lippert said, considering everything the runtime is doing already, something will always be a worse bottleneck than a choice of composition. Anything so bizarre as to move faster purely from inheritance probably shouldn't be programmed in C#, which is definitely already affecting performance.

@AJMansfield 2014-02-12 00:37:22

@Superbest What for, Kerbal Space Program?

@Eric Lippert 2014-02-12 17:33:45

@SeanLynch: The author of The Oatmeal lives here in Seattle and I strongly suspect that the Piggers are actually the Seahawks. So yes PIGGERS WENT ALL THE WAY THIS YEAR WOOO!

@Eric Lippert 2014-02-12 21:32:33

@MikeGraham: The point of the exercise was not to advocate for a particular methodology for creating a type hierarchy, but rather to respond to the original poster's assertion that "any human mind" would logically consider a football team to be a list of players. If you disagree with that assertion then I invite you to write an answer that you think answers the question better.

@gilly3 2014-02-13 22:02:56

I totally agree with your last statement. Clearly everyone here agrees - a football team is not a list of players. The problem is that it completely misses the point of the question. It was an unfortunate example to use in the question because everyone's visceral response to it completely obscured the actual, valid question. It might have been better to say he wanted to extend List<T> for some small functionality in his Roster property. Then, we might have been able to discuss the reasons why MS recommends against extending List<T> and when it would be appropriate to do so anyway.

@Disillusioned 2014-02-16 07:12:50

@gilly3 Based on OP's comment here, it seems the chosen example, and point of the question are actually spot on.

@Jonathan Leonard 2014-02-17 06:54:54

@EricLippert Extension methods almost eliminated the main objection here to composition (i.e., the verbosity of wrapping) but the ability to implement interfaces with them didn't quite make the cut. Scala's traits would work nicely here though.

@myermian 2014-02-17 15:36:52

@EricLippert: I disagree with the part of your answer that is after the update. Wouldn't it be better to create your business object so that it represents an instance of a football team, and use data warehousing techniques to get at the business intelligence/history?

@Eric Lippert 2014-02-18 01:50:08

@m-y: Better by what metric?

@Eric Lippert 2015-02-02 15:59:20

The Piggers did not quite go all the way this year. Oh well.

@Ben 2015-04-13 16:26:48

Sorry Eric, I disagree with your assumption that a team is not a list of players. I would suggest that a football team is a list of players while a football club has a list of players. A club has managers and history etc. but a team may only exist for a single game. I think answers and comments have been using the terms 'team' and 'club' interchangeably and this is leading to confusion. The definition of the word team: "A group of players ...", so I think a team is a list of players.

@Ben 2015-04-13 16:33:58

I would also like to say that I appreciate your explanation about the difference between a mechanism and a business object. This is something I found very helpful thanks.

@Ben 2015-04-13 17:23:12

Here is another quote: "A football team is the collective name given to a group of players ... Such teams could be selected to play in a match ... to represent a football club". In a sporting context, the word team is the collective noun for a group of players. Therefore I think it is reasonable to say that a team is a list of players (or perhaps more accurately a collection of players).

@phoog 2015-05-29 07:41:09

@Ben The distinction you make between "team" and "club" would be lost on most people without first defining them for a specific context. Before I read your comment, if you asked me the difference between a football team and a football club, I'd have said that a football team plays with an elongated ball on a field with lots of lines in North America, while a football club plays with a spherical ball on a pitch with few lines in the UK. In other words, that the difference between a team and a club is roughly analogous to that between a truck and a lorry.

@Kevin Laity 2017-04-11 15:06:11

One point I haven't seen captured is that a football team is likely to sooner or later contain lists of things other than players. (Equipment? Other employees?) And a list that has more lists tacked onto the side of it (and not as children of that list) would be weird. I've done this before. It was weird.

@O. R. Mapper 2017-04-26 05:46:55

No offense intended, but in my opinion, the update part goes too far. Sure, given the appropriate use case, that is a good reason to not model the team as a list of players, but assuming use cases that are far beyond what is described as the problem at hand is not useful for finding and understanding an appropriate design for the current problem. After all, one might point out that you actually rather need something like a graph of objects representing the atoms that make up the players, because you may be simulating the physics involved in a football game (including dirt that is ...

@O. R. Mapper 2017-04-26 05:50:02

... swapped from one player's skin to another over time). Or, one could point out that a "collection of historical facts" is insufficient, because it does not contain information about geographically distributed public perception (to answers questions such as "What part of the public assumed that player X was still a member of team Y a year after another part of the public considered X to have resigned from the team?"). As such, I fail to see the points made in your update as a generally applicable "reason to not model a football team as a list of players".

@Charleh 2018-02-19 12:50:50

@Superbest "why am I doing this?" is the most important question a programmer/developer should ask! Anyone that asks that question every time they program something is on their or already a good programmer.

@Robert Achmann 2018-08-15 15:22:38

I wouldn't make the player list readonly... Draft picks are coming up and you have Ereck Flowers in your roster, and your list is read-only? Good luck with that.

@Clearer 2018-09-24 10:22:55

I'm sure that other answers already has this bit: even if you where to create a model where you are only interested in the current set of players, a list would be wrong too. A list may contain duplicates and having a list in the first places indicates that order is important. You don't want duplicates in a roster and in a roster, the order is not important; instead of using a list, you should use a set. If you want to map attributes to a player, you should use a map/dictionary, rather than rely on something as fickle as a list.

@jmbpiano 2018-10-16 17:48:48

@RobertAchmann The point is to make the public property exposing the list read-only. The internal representation of the list would, of course, remain mutable to the Team class.

@marsh-wiggle 2019-07-20 14:55:13

Problems with serializing

One aspect is missing. Classes that inherit from List<> can't be serialized correctly using XmlSerializer. In that case DataContractSerializer must be used instead, or an own serializing implementation is needed.

public class DemoList : List<Demo>
    // using XmlSerializer this properties won't be seralized:
    string AnyPropertyInDerivedFromList { get; set; }     

public class Demo
    // this properties will be seralized
    string AnyPropetyInDemo { get; set; }  

Further reading: When a class is inherited from List<>, XmlSerializer doesn't serialize other attributes

@stefan.schwetschke 2014-02-11 10:10:09

It depends on the context

When you consider your team as a list of players, you are projecting the "idea" of a foot ball team down to one aspect: You reduce the "team" to the people you see on the field. This projection is only correct in a certain context. In a different context, this might be completely wrong. Imagine you want to become a sponsor of the team. So you have to talk to the managers of the team. In this context the team is projected to the list of its managers. And these two lists usually don't overlap very much. Other contexts are the current versus the former players, etc.

Unclear semantics

So the problem with considering a team as a list of its players is that its semantic depends on the context and that it cannot be extended when the context changes. Additionally it is hard to express, which context you are using.

Classes are extensible

When you using a class with only one member (e.g. IList activePlayers), you can use the name of the member (and additionally its comment) to make the context clear. When there are additional contexts, you just add an additional member.

Classes are more complex

In some cases it might be overkill to create an extra class. Each class definition must be loaded through the classloader and will be cached by the virtual machine. This costs you runtime performance and memory. When you have a very specific context it might be OK to consider a football team as a list of players. But in this case, you should really just use a IList , not a class derived from it.

Conclusion / Considerations

When you have a very specific context, it is OK to consider a team as a list of players. For example inside a method it is completely OK to write:

IList<Player> footballTeam = ...

When using F#, it can even be OK to create a type abbreviation:

type FootballTeam = IList<Player>

But when the context is broader or even unclear, you should not do this. This is especially the case when you create a new class whose context in which it may be used in the future is not clear. A warning sign is when you start to add additional attributes to your class (name of the team, coach, etc.). This is a clear sign that the context where the class will be used is not fixed and will change in the future. In this case you cannot consider the team as a list of players, but you should model the list of the (currently active, not injured, etc.) players as an attribute of the team.

@cdiggins 2018-10-07 05:01:01

Prefer Interfaces over Classes

Classes should avoid deriving from classes and instead implement the minimal interfaces necessary.

Inheritance breaks Encapsulation

Deriving from classes breaks encapsulation:

  • exposes internal details about how your collection is implemented
  • declares an interface (set of public functions and properties) that may not be appropriate

Among other things this makes it harder to refactor your code.

Classes are an Implementation Detail

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.

The Smaller the Interface the more Flexible the Code

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.

How to Choose Interfaces

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:

  • Do I need to have the count? If not consider implementing IEnumerable<T>
  • Is this collection going to change after it has been initialized? If not consider IReadonlyList<T>.
  • Is it important that I can access items by index? Consider ICollection<T>
  • Is the order in which I add items to the collection important? Maybe it is an ISet<T>?
  • If you indeed want these thing then go ahead and implement IList<T>.

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.

Notes about Avoiding Boilerplate

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.

@cmxl 2020-08-09 08:15:04

Creating an interface will prevent you from deriving from "wrong" classes in the first place, because you then exactly see what behaviour you are looking for. That was my first thought tbh.

@myermian 2014-02-11 03:26:26

Wow, your post has an entire slew of questions and points. Most of the reasoning you get from Microsoft is exactly on point. Let's start with everything about List<T>

  • List<T> is highly optimized. Its main usage is to be used as a private member of an object.
  • Microsoft did not seal it because sometimes you might want to create a class that has a friendlier name: class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... }. Now it's as easy as doing var list = new MyList<int, string>();.
  • CA1002: Do not expose generic lists: Basically, even if you plan to use this app as the sole developer, it's worthwhile to develop with good coding practices, so they become instilled into you and second nature. You are still allowed to expose the list as an IList<T> if you need any consumer to have an indexed list. This let's you change the implementation within a class later on.
  • Microsoft made Collection<T> very generic because it is a generic concept... the name says it all; it is just a collection. There are more precise versions such as SortedCollection<T>, ObservableCollection<T>, ReadOnlyCollection<T>, etc. each of which implement IList<T> but not List<T>.
  • Collection<T> allows for members (i.e. Add, Remove, etc.) to be overridden because they are virtual. List<T> does not.
  • The last part of your question is spot on. A Football team is more than just a list of players, so it should be a class that contains that list of players. Think Composition vs Inheritance. A Football team has a list of players (a roster), it isn't a list of players.

If I were writing this code the class would probably look something like so:

public class FootballTeam
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
        get { return _roster; }

    // Yes. I used LINQ here. This is so I don't have to worry about
    // _roster.Length vs _roster.Count vs anything else.
    public int PlayerCount
        get { return _roster.Count(); }

    // Any additional members you want to expose/wrap.

@Superbest 2014-02-11 05:11:58

"your post has an entire sleuth of questions" - Well, I did say I was thoroughly confused. Thanks for linking to @Readonly's question, I now see that a big part of my question is a special case of the more general Composition vs. Inheritance problem.

@peterh - Reinstate Monica 2014-02-11 08:44:08

FootballGroup : List<FootballPlayers>, it seems absolutely acceptable and very efficient to me. There weren't any real technical reasons explained, why this construction didn't deserve the right to live, only phylosophical arguments.

@Brian 2014-02-11 14:05:43

I am skeptical of your argument that List is unsealed to allow for friendlier names. That is what using aliases are for.

@myermian 2014-02-11 16:33:31

@Brian: Except that you can't create a generic using alias, all types must be concrete. In my example, List<T> is extended as a private inner class which uses generics to simplify the usage and declaration of List<T>. If the extension was merely class FootballRoster : List<FootballPlayer> { }, then yes I could have used an alias instead. I guess it's worth noting that you could add additional members/functionality, but it is limited since you cannot override important members of List<T>.

@Mark Hurd 2014-02-12 02:10:15

If this were Programmers.SE, I'd agree with the current range of answer votes, but, as it is an SO post, this answer at least attempts to answer the C# (.NET) specific issues, even though there are definite general design issues.

@myermian 2014-02-12 02:25:32

@jberger: I always prefer to use the LINQ version of Enumerable.Count<TSource>(IEnumerable<TSource>) so that if I ever change the underlying type it will always work as long as it is an IEnumerable<T>. There is a very negligible performance hit vs just using List<T>.Count. One day though, I might decide I want a new data structure that implements IEnumerable<T>, but nothing else, who knows. See for more info. :)

@Navin 2014-02-12 03:54:06

Collection<T> allows for members (i.e. Add, Remove, etc.) to be overridden Now that is a good reason not to inherit from List. The others answers have way too much philosophy.

@Groo 2014-02-12 13:00:17

@Navin: that doesn't apply for the List<T> class. Collection<T> class is supposed to be inherited in order to extend its functionality as needed.

@myermian 2014-02-12 13:47:07

@Groo: You do realize that it isn't going from O(1) to O(n) if the sequence is a type of ICollection<T> or ICollection. In those cases, the LINQ function just reverts back to using the ICollection<T>.Count/ICollection.Count methods.

@Groo 2014-02-12 14:34:46

@m-y: Yeah, sorry, that part's correct, I didn't think that through. It also does this optimization with First, Last, and other extension methods which can benefit from accessing ICollection members directly, so there's actually not much harm in doing it.

@batty 2014-02-16 16:28:28

@m-y: I suspect you mean "slew" of questions, since a sleuth is a detective.

@Jonathan Allen 2016-04-19 22:16:44

If you write public IList<T> Roster the consumer pays a performance penalty. It is much faster to use a for-each over a List<T> than an IList<T>, like more than double on my machine.

@myermian 2016-04-19 23:53:30

@JonathanAllen: That's not how the foreach keyword works (see In this case, the underlying type is still a List<T> which means that it will call GetEnumerator() on the type and use that enumerator to iterate over.

@Jonathan Allen 2016-04-20 19:58:40

If you call List<T>.GetEnumerator(), you get a struct named Enumerator. If you call IList<T>.GetEnumerator(), you get a variable of type IEnumerable<T> that happens to contain the boxed version of said struct. In the former case, foreach will call the methods directly. In the latter, all calls have to be virtually dispatched via the interface, making each call slower. (Roughly twice as slow on my machine.)

@myermian 2016-04-20 21:11:12

@JonathanAllen: Any call to GetEnumerator() will return a type that implements IEnumerable<T> (or IEnumerable) depending on usage. The underlying type though (in this instance) is the same. It is the Enumerator struct. See the reference source:… ... they all return the same type.

@Jonathan Allen 2016-04-20 23:39:48

You are missing the point entirely. foreach doesn't care whether or not Enumerator implements the IEnumerable<T> interface. If it can find the methods it needs on Enumerator, then it won't use IEnumerable<T>. If you actually decompile the code, you can see how foreach avoids using virtual dispatch calls when iterating over List<T>, but will with IList<T>.

@Jonathan Allen 2016-04-20 23:41:33

In fact, GetEnumerator doesn't even need to return something that is IEnumerable. You could create your own enumerator that has the right methods, but doesn't implement that interface, and foreach will still work.

@Null511 2018-01-20 02:43:24

While I don't have a complex comparison as most of these answers do, I would like to share my method for handling this situation. By extending IEnumerable<T>, you can allow your Team class to support Linq query extensions, without publicly exposing all the methods and properties of List<T>.

class Team : IEnumerable<Player>
    private readonly List<Player> playerList;

    public Team()
        playerList = new List<Player>();

    public Enumerator GetEnumerator()
        return playerList.GetEnumerator();


class Player

@Eniola 2016-07-09 17:14:30

I think I don't agree with your generalization. A team isn't just a collection of players. A team has so much more information about it - name, emblem, collection of management/admin staff, collection of coaching crew, then collection of players. So properly, your FootballTeam class should have 3 collections and not itself be a collection; if it is to properly model the real world.

You could consider a PlayerCollection class which like the Specialized StringCollection offers some other facilities - like validation and checks before objects are added to or removed from the internal store.

Perhaps, the notion of a PlayerCollection betters suits your preferred approach?

public class PlayerCollection : Collection<Player> 

And then the FootballTeam can look like this:

public class FootballTeam 
    public string Name { get; set; }
    public string Location { get; set; }

    public ManagementCollection Management { get; protected set; } = new ManagementCollection();

    public CoachingCollection CoachingCrew { get; protected set; } = new CoachingCollection();

    public PlayerCollection Players { get; protected set; } = new PlayerCollection();

@ArturoTena 2014-02-11 18:03:06

What is the correct C# way of representing a data structure...

Remeber, "All models are wrong, but some are useful." -George E. P. Box

There is no a "correct way", only a useful one.

Choose one that is useful to you and/your users. That's it. Develop economically, don't over-engineer. The less code you write, the less code you will need to debug. (read the following editions).

-- Edited

My best answer would be... it depends. Inheriting from a List would expose the clients of this class to methods that may be should not be exposed, primarily because FootballTeam looks like a business entity.

-- Edition 2

I sincerely don't remember to what I was referring on the “don't over-engineer” comment. While I believe the KISS mindset is a good guide, I want to emphasize that inheriting a business class from List would create more problems than it resolves, due abstraction leakage.

On the other hand, I believe there are a limited number of cases where simply to inherit from List is useful. As I wrote in the previous edition, it depends. The answer to each case is heavily influenced by both knowledge, experience and personal preferences.

Thanks to @kai for helping me to think more precisely about the answer.

@ArturoTena 2014-02-11 18:23:11

My best answer would be... it depends. Inheriting from a List would expose the clients of this class to methods that may be should not be exposed, primarily because FootballTeam looks like a business entity. I don't know if I should edit this answer or post another, any idea?

@Disillusioned 2014-02-15 18:59:32

The bit about "inheriting from a List would expose the clients of this class to methods that may be should not be exposed" is precisely what makes inheriting from List an absolute no-no. Once exposed, they gradually get abused and mis-used over time. Saving time now by "developing economically" can easily lead to tenfold the savings lost in future: debugging the abuses and eventually refactoring to fix the inheritance. The YAGNI principle can also be thought of as meaning: You Ain't Gonna Need all those methods from List, so don't expose them.

@sara 2015-09-15 15:40:19

"don't over-engineer. The less code you write, the less code you will need to debug." <-- I think this is misleading. Encapsulation and composition over inheritance is NOT over-engineering and it does not cause more need for debugging. By encapsulating you are LIMITING the number of ways clients can use (and abuse) your class and thus you have fewer entry points that need testing and input validation. Inheriting from List because it's quick and easy and thus would lead to fewer bugs is plain wrong, it's just bad design and bad design leads to a lot more bugs than "over engineering".

@ArturoTena 2015-09-15 22:20:21

@kai I agree with you in every point. I sincerely don't remember to what I was referring on the “don't over-engineer” comment. OTOH, I believe there are a limited number of cases where simply to inherit from List is useful. As I wrote in the later edition, it depends. The answer to each case is heavily influenced by both knowledge, experience and personal preferences. Like everything in life. ;-)

@Alexey 2015-04-27 11:35:13

I just wanted to add that Bertrand Meyer, the inventor of Eiffel and design by contract, would have Team inherit from List<Player> without so much as batting an eyelid.

In his book, Object-Oriented Software Construction, he discusses the implementation of a GUI system where rectangular windows can have child windows. He simply has Window inherit from both Rectangle and Tree<Window> to reuse the implementation.

However, C# is not Eiffel. The latter supports multiple inheritance and renaming of features. In C#, when you subclass, you inherit both the interface and the implemenation. You can override the implementation, but the calling conventions are copied directly from the superclass. In Eiffel, however, you can modify the names of the public methods, so you can rename Add and Remove to Hire and Fire in your Team. If an instance of Team is upcast back to List<Player>, the caller will use Add and Remove to modify it, but your virtual methods Hire and Fire will be called.

@Mauro Sampietro 2015-07-07 13:57:28

Here the problem is not multiple inheritance, mixins (etc..) or how to deal with their absence. In any language, even in human language a team is not a list of people but composed of a list of people. This is an abstract concept. If Bertrand Meyer or anyone manages a team by subclassing List is doing wrong. You should subclass a List if you want a more specific kind of list. Hope you agree.

@Alexey 2015-07-07 19:12:31

That depends on what inheritance is to you. By itself, it is an abstract operation, it doesn't mean two classes are in an "is a special kind of" relationship, even though it is the mainstream interpretation. However, inheritance can be treated as a mechanism for implementation reuse if the language design supports it, even though composition is the pereferred alternative nowadays.

@Mark Brackett 2015-04-06 21:35:20

Just because I think the other answers pretty much go off on a tangent of whether a football team "is-a" List<FootballPlayer> or "has-a" List<FootballPlayer>, which really doesn't answer this question as written.

The OP chiefly asks for clarification on guidelines for inheriting from List<T>:

A guideline says that you shouldn't inherit from List<T>. Why not?

Because List<T> has no virtual methods. This is less of a problem in your own code, since you can usually switch out the implementation with relatively little pain - but can be a much bigger deal in a public API.

What is a public API and why should I care?

A public API is an interface you expose to 3rd party programmers. Think framework code. And recall that the guidelines being referenced are the ".NET Framework Design Guidelines" and not the ".NET Application Design Guidelines". There is a difference, and - generally speaking - public API design is a lot more strict.

If my current project does not and is not likely to ever have this public API, can I safely ignore this guideline? If I do inherit from List and it turns out I need a public API, what difficulties will I have?

Pretty much, yeah. You may want to consider the rationale behind it to see if it applies to your situation anyway, but if you're not building a public API then you don't particularly need to worry about API concerns like versioning (of which, this is a subset).

If you add a public API in the future, you will either need to abstract out your API from your implementation (by not exposing your List<T> directly) or violate the guidelines with the possible future pain that entails.

Why does it even matter? A list is a list. What could possibly change? What could I possibly want to change?

Depends on the context, but since we're using FootballTeam as an example - imagine that you can't add a FootballPlayer if it would cause the team to go over the salary cap. A possible way of adding that would be something like:

 class FootballTeam : List<FootballPlayer> {
     override void Add(FootballPlayer player) {
        if (this.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");

Ah...but you can't override Add because it's not virtual (for performance reasons).

If you're in an application (which, basically, means that you and all of your callers are compiled together) then you can now change to using IList<T> and fix up any compile errors:

 class FootballTeam : IList<FootballPlayer> {
     private List<FootballPlayer> Players { get; set; }

     override void Add(FootballPlayer player) {
        if (this.Players.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");
     /* boiler plate for rest of IList */

but, if you've publically exposed to a 3rd party you just made a breaking change that will cause compile and/or runtime errors.

TL;DR - the guidelines are for public APIs. For private APIs, do what you want.

@Sam Leach 2014-02-12 11:41:14

What if the FootballTeam has a reserves team along with the main team?

class FootballTeam
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }

How would you model that with?

class FootballTeam : List<FootballPlayer> 
    public string TeamName; 
    public int RunningTotal 

The relationship is clearly has a and not is a.

or RetiredPlayers?

class FootballTeam
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
    List<FootballPlayer> RetiredPlayers { get; set; }

As a rule of thumb, if you ever want to inherit from a collection, name the class SomethingCollection.

Does your SomethingCollection semantically make sense? Only do this if your type is a collection of Something.

In the case of FootballTeam it doesn't sound right. A Team is more than a Collection. A Team can have coaches, trainers, etc as the other answers have pointed out.

FootballCollection sounds like a collection of footballs or maybe a collection of football paraphernalia. TeamCollection, a collection of teams.

FootballPlayerCollection sounds like a collection of players which would be a valid name for a class that inherits from List<FootballPlayer> if you really wanted to do that.

Really List<FootballPlayer> is a perfectly good type to deal with. Maybe IList<FootballPlayer> if you are returning it from a method.

In summary

Ask yourself

  1. Is X a Y? or Has X a Y?

  2. Do my class names mean what they are?

@supercat 2014-09-11 20:48:27

If the type of every player would classify it as belonging to either DefensivePlayers, OffensivePlayers, or OtherPlayers, it might be legitimately useful to have a type which could be used by code which expects a List<Player> but also included members DefensivePlayers, OffsensivePlayers, or SpecialPlayers of type IList<DefensivePlayer>, IList<OffensivePlayer>, and IList<Player>. One could use a separate object to cache the separate lists, but encapsulating them within the same object as the main list would seem cleaner [use the invalidation of a list enumerator...

@supercat 2014-09-11 20:48:55 a cue for the fact that the main list has changed and the sub-lists will need to be regenerated when they're next accessed].

@sara 2016-02-01 09:56:00

While I agree with the point made, it really tears my soul apart to see someone give design advice and suggest exposing a concrete List<T> with a public getter and setter in a business object :(

@Tim B 2014-02-11 10:58:39

This is a classic example of composition vs inheritance.

In this specific case:

Is the team a list of players with added behavior


Is the team an object of its own that happens to contain a list of players.

By extending List you are limiting yourself in a number of ways:

  1. You cannot restrict access (for example, stopping people changing the roster). You get all the List methods whether you need/want them all or not.

  2. What happens if you want to have lists of other things as well. For example, teams have coaches, managers, fans, equipment, etc. Some of those might well be lists in their own right.

  3. You limit your options for inheritance. For example you might want to create a generic Team object, and then have BaseballTeam, FootballTeam, etc. that inherit from that. To inherit from List you need to do the inheritance from Team, but that then means that all the various types of team are forced to have the same implementation of that roster.

Composition - including an object giving the behavior you want inside your object.

Inheritance - your object becomes an instance of the object that has the behavior you want.

Both have their uses, but this is a clear case where composition is preferable.

@Cruncher 2014-02-12 13:47:46

Expanding on #2, it doesn't make sense for a List to have-a List.

@Mauro Sampietro 2016-08-09 13:16:36

First, the question has nothing to do with composition vs inheritance. The answer is OP doesn't want to implement a more specific kind of list, so he should not extend List<>. I'm twisted cause you have very high scores on stackoverflow and should know this clearly and people trust what you say, so by now a min of 55 people who upvoted and whose idea are confused believe either a way or the other is ok to build a system but clearly it's not! #no-rage

@Tim B 2016-08-09 14:24:30

@sam He wants the behaviour of the list. He has two choices, he can extend list (inheritance) or he can include a list inside his object (composition). Perhaps you have misunderstood part of either the question or the answer rather than 55 people being wrong and you right? :)

@Mauro Sampietro 2016-08-10 07:17:29

@TimB ..are we really talking about the same thing?!

@Tim B 2016-08-10 08:55:33

Yes. It's a degenerate case with only one super and sub but I'm giving the more general explanation for his specific question. He may only have one team and that may always have one list but the choice is still to inherit the list or include it as an internal object. Once you start including multiple types of team (football. Cricket. Etc) and they start being more than just a list of players you see how you have the full composition vs inheritance question. Looking at the big picture is important in cases like this to avoid wrong choices early that then mean a lot of refactoring later.

@Andrew Rondeau 2017-08-25 19:24:25

This is a composition vs inheritance question. The topics that the OP brought up ultimately show that, in his thinking, he doesn't really understand the difference between "is-a" and "has-a" relationships. The OP also misunderstands behaviors versus implementations.

@Julia McGuigan 2018-02-01 17:42:36

The OP did not ask for Composition, but the use case is a clear-cut example of X:Y problem of which one of the 4 object oriented programming principles is broken, misused, or not fully understood. The more clear answer is either write a specialized collection like a Stack or Queue (which does not appear to fit the use case) or to understand composition. A Football team is NOT a List of Football players. Whether the OP asked for it explicitly or not is irrelevant, without understanding Abstraction, Encapsulation, Inheritance, and Polymorphism they will not understand the answer, ergo, X:Y amok

@AustinWBryan 2018-05-08 23:12:09

2. Is probably the best reason of all the above posts. You would really screw yourself over if you decided you want to have other lists on it.

@QuentinUK 2014-02-18 02:53:22

What the guidelines say is that the public API should not reveal the internal design decision of whether you are using a list, a set, a dictionary, a tree or whatever. A "team" is not necessarily a list. You may implement it as a list but users of your public API should use you class on a need to know basis. This allows you to change your decision and use a different data structure without affecting the public interface.

@Superbest 2014-02-18 17:19:19

In retrospect, after the explanation of @EricLippert and others, you've actually given a great answer for the API part of my question - in addition to what you said, if I do class FootballTeam : List<FootballPlayers>, users of my class will be able to tell I've inherited from List<T> by using reflection, seeing the List<T> methods that don't make sense for a football team, and being able to use FootballTeam into List<T>, so I would be revealing implementation details to the client (unnecessarily).

@Nicolas Dorier 2014-02-12 13:57:08

My dirty secret: I don't care what people say, and I do it. .NET Framework is spread with "XxxxCollection" (UIElementCollection for top of my head example).

So what stops me saying:


When I find it better than


Moreover, my PlayerCollection might be used by other class, like "Club" without any code duplication.


Best practices of yesterday, might not be the one of tomorrow. There is no reason behind most best practices, most are only wide agreement among the community. Instead of asking the community if it will blame you when you do that ask yourself, what is more readable and maintainable?




Really. Do you have any doubt? Now maybe you need to play with other technical constraints that prevent you to use List<T> in your real use case. But don't add a constraint that should not exist. If Microsoft did not document the why, then it is surely a "best practice" coming from nowhere.

@Superbest 2014-02-12 13:59:47

While it is important to have the courage and initiative to challenge the accepted wisdom when appropriate, I think that it is wise to first understand why the accepted wisdom has come to be accepted in the first place, before embarking to challenge it. -1 because "Ignore that guideline!" is not a good answer to "Why does this guideline exist?" Incidentally, the community did not just "blame me" in this case, but provided a satisfactory explanation that succeeded in persuading me.

@Nicolas Dorier 2014-02-12 14:02:51

It is actually, when no explanation is made, your only way is pushing the boundary and testing not being afraid of infraction. Now. Ask yourself, even if List<T> was not a good idea. How much a pain would it be to change inheritance from List<T> to Collection<T> ? A guess : Less time than posting on the forum, whatever the length of your code with refactoring tools. Now it is a good question, but not a practical one.

@Superbest 2014-02-12 14:31:16

To clarify: I'm certainly not waiting on SO's blessing to inherit from whatever I want however I want it, but the point of my question was to understand the considerations that should go into this decision, and why so many experienced programmers seem to have decided the opposite of what I did. Collection<T> is not the same as List<T> so it may require quite a bit of work to verify in a large-ish project.

@Nicolas Dorier 2014-02-12 21:47:39

Sorry SuperBest, I did not implied your question was useless, nor that that you rely too much on community. This is a good question, or would not have bothered to give a response. My point is if someone says : "don't do that" and does not explain why, you should ignore the advice while considering the impact on what happen if you are wrong. Changing List<T> to Collection<T> is not a problem with today refactoring tools and patterns. (even for large project) As you said, List<T> have some advantage on Collection<T>, the current accepted response is a philosophical argument, not a practical one.

@Sam Leach 2014-02-14 20:54:52

team.ByName("Nicolas") means "Nicolas" is the name of the team.

@Nicolas Dorier 2014-03-12 15:41:44

Good remark Sam, it emphasize that this method is harder to read and understand. :)

@sara 2015-09-15 15:45:45

"don't add a constraint that should not exist" Au contraire, don't expose anything you don't have a good reason to expose. This is not purism or blind zealotry, nor is it the latest design fashion trend. It is basic object oriented design 101, beginner level. It's not a secret why this "rule" exists, it is the result of several decades of experience.

@Tvde1 2019-06-18 07:53:21

If we follow Demeter's Law, it should be called team.GetPlayerByName("Nicolas") or team.GetRetiredPlayerByName("John").

@user1852503 2014-02-16 07:56:56

Let me rewrite your question. so you might see the subject from a different perspective.

When I need to represent a football team, I understand that it is basically a name. Like: "The Eagles"

string team = new string();

Then later I realized teams also have players.

Why can't I just extend the string type so that it also holds a list of players?

Your point of entry into the problem is arbitrary. Try to think what does a team have (properties), not what it is.

After you do that, you could see if it shares properties with other classes. And think about inheritance.

@Superbest 2014-02-16 20:34:33

That's a good point - one could think of the team as just a name. However, if my application aims to work with the actions of the players, then that thinking is a bit less obvious. Anyway, it seems that the issue comes down to composition vs. inheritance in the end.

@user1852503 2014-02-17 05:59:28

That is one meritorious way to look at a side note please consider this: A man of wealth owns several football teams, he gives one to a friend as a gift, the friend changes the name of the team, fires the coach, and replaces all the players. The friends team meets the men's team on the green and as the men's team is losing he says "I can't believe you are beating me with the team I gave you!" is the man correct? how would you check this?

@Ivan Nikitin 2014-02-16 07:59:49

If your class users need all the methods and properties** List has, you should derive your class from it. If they don't need them, enclose the List and make wrappers for methods your class users actually need.

This is a strict rule, if you write a public API, or any other code that will be used by many people. You may ignore this rule if you have a tiny app and no more than 2 developers. This will save you some time.

For tiny apps, you may also consider choosing another, less strict language. Ruby, JavaScript - anything that allows you to write less code.

@Shital Shah 2014-02-16 07:50:31

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.

@Disillusioned 2014-02-15 20:05:59

There are a lot excellent answers here, but I want to touch on something I didn't see mentioned: Object oriented design is about empowering objects.

You want to encapsulate all your rules, additional work and internal details inside an appropriate object. In this way other objects interacting with this one don't have to worry about it all. In fact, you want to go a step further and actively prevent other objects from bypassing these internals.

When you inherit from List, all other objects can see you as a List. They have direct access to the methods for adding and removing players. And you'll have lost your control; for example:

Suppose you want to differentiate when a player leaves by knowing whether they retired, resigned or were fired. You could implement a RemovePlayer method that takes an appropriate input enum. However, by inheriting from List, you would be unable to prevent direct access to Remove, RemoveAll and even Clear. As a result, you've actually disempowered your FootballTeam class.

Additional thoughts on encapsulation... You raised the following concern:

It makes my code needlessly verbose. I must now call my_team.Players.Count instead of just my_team.Count.

You're correct, that would be needlessly verbose for all clients to use you team. However, that problem is very small in comparison to the fact that you've exposed List Players to all and sundry so they can fiddle with your team without your consent.

You go on to say:

It just plain doesn't make any sense. A football team doesn't "have" a list of players. It is the list of players. You don't say "John McFootballer has joined SomeTeam's players". You say "John has joined SomeTeam".

You're wrong about the first bit: Drop the word 'list', and it's actually obvious that a team does have players.
However, you hit the nail on the head with the second. You don't want clients calling ateam.Players.Add(...). You do want them calling ateam.AddPlayer(...). And your implemention would (possibly amongst other things) call Players.Add(...) internally.

Hopefully you can see how important encapsulation is to the objective of empowering your objects. You want to allow each class to do its job well without fear of interference from other objects.

@El Zorko 2014-02-11 22:27:53

Design > Implementation

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:

  • What data does this object comprise in the model I'm creating?
  • What behaviour does this object exhibit in that model?
  • How might this change in future?

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.

Consider Design Specifics

Take a look at List<T> and IList<T> on MSDN or Visual Studio. See what methods and properties they expose. Do these methods all look like something someone would want to do to a FootballTeam in your view?

Does footballTeam.Reverse() make sense to you? Does footballTeam.ConvertAll<TOutput>() look like something you want?

This isn't a trick question; the answer might genuinely be "yes". If you implement/inherit List<Player> or IList<Player>, you're stuck with them; if that's ideal for your model, do it.

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<Player>

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<Player> 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<Player> is a perfectly valid option to consider.

You might feel that none of these interfaces are useful in your model at all. This is less likely (IEnumerable<T> is useful in many situations) but it's still possible.

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.

Move on to Implementation

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.

A Thought Experiment

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<Player>, 

Design notwithstanding, at this point in C# you won't be able to implement all of these by inheriting from List<T> anyway, since C# "only" supports single inheritance. (If you've tried this malarky in C++, you may consider this a Good Thing.) Implementing one collection via inheritance and one via composition is likely to feel dirty. And properties such as Count become confusing to users unless you implement ILIst<Player>.Count and IList<StaffMember>.Count etc. explicitly, and then they're just painful rather than confusing. You can see where this is going; gut feeling whilst thinking down this avenue may well tell you it feels wrong to head in this direction (and rightly or wrongly, your colleagues might also if you implemented it this way!)

The Short Answer (Too Late)

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.

@Cruncher 2014-02-11 21:42:45

Does allowing people to say

myTeam.subList(3, 5);

make any sense at all? If not then it shouldn't be a List.

@Sam Leach 2014-02-14 21:01:16

It might if you called it myTeam.subTeam(3, 5);

@Cruncher 2014-02-18 15:52:42

@SamLeach Assuming that's true, then you will still need composition not inheritance. As subList won't even return a Team anymore.

@FireCubez 2020-05-17 18:32:54

This convinced me more than any other answer. +1

@Satyan Raina 2014-02-11 16:56:35

As everyone has pointed out, a team of players is not a list of players. This mistake is made by many people everywhere, perhaps at various levels of expertise. Often the problem is subtle and occasionally very gross, as in this case. Such designs are bad because these violate the Liskov Substitution Principle. The internet has many good articles explaining this concept e.g.,

In summary, there are two rules to be preserved in a Parent/Child relationship among classes:

  • a Child should require no characteristic less than what completely defines the Parent.
  • a Parent should require no characteristic in addition to what completely defines the Child.

In other words, a Parent is a necessary definition of a child, and a child is a sufficient definition of a Parent.

Here is a way to think through ones solution and apply the above principle that should help one avoid such a mistake. One should test ones hypothesis by verifying if all the operations of a parent class are valid for the derived class both structurally and semantically.

  • Is a football team a list of football players? ( Do all properties of a list apply to a team in the same meaning)
    • Is a team a collection of homogenous entities? Yes, team is a collection of Players
    • Is the order of inclusion of players descriptive of the state of the team and does the team ensure that the sequence is preserved unless explicitly changed? No, and No
    • Are players expected to be included/dropped based on their sequencial position in the team? No

As you see, only the first characteristic of a list is applicable to a team. Hence a team is not a list. A list would be a implementation detail of how you manage your team, so it should only be used to store the player objects and be manipulated with methods of Team class.

At this point I'd like to remark that a Team class should, in my opinion, not even be implemented using a List; it should be implemented using a Set data structure (HashSet, for example) in most cases.

@Fred Mitchell 2014-02-11 21:26:13

Nice catch on the List versus Set. That seems to be an error only too commonly made. When there can only be one instance of an element in a collection, some sort of set, or dictionary should be a preferred candidate for the implementation. It is ok to have a team with two players with the same name. It is not ok to have the one player included twice at the same time.

@Tony Delroy 2014-02-17 10:26:41

+1 for some good points, though it's more important to ask "can a data structure reasonably support everything useful (ideally short and long term)", rather than "does the data structure do more than is useful".

@Satyan Raina 2014-02-17 11:07:06

@TonyD Well, the points I am raising is not that one should check " if the data structure does more than what is useful". It is to check "If the Parent data structure does something that is irrelevant, meaningless or counter-intuitive to the behavior what the Child class might imply".

@Tony Delroy 2014-02-17 11:54:14

@SatyanRaina: and of those, there's nothing wrong with being able to do extra things that are irrelevant or meaningless as long as they don't actually compromise the operations that are meaningful and will be used. For example, if a balanced binary tree happens to iterate elements in key-sorted order and that's not necessary, it's not actually a problem either. E.g. "order of inclusion of players [being] descriptive of the state of the team": if no order is required, or the multiple orderings likely wanted can still be efficiently conveniently formed, a particular default order does no harm.

@Satyan Raina 2014-02-17 14:51:35

@TonyD There is actually a problem with having irrelevant characteristics derived from a Parent as it would fail the negative tests in many cases. A programmer could extend Human{ eat(); run(); write();} from a Gorilla{ eat(); run(); swing();} thinking there is nothing wrong with a human with an extra feature of being able to swing. And then in a game world your human suddenly starts to bypass all supposed land hurdles by just swinging over the trees. Unless explicitly specified, a practical Human should not be able to swing. Such a design leaves the api very open to abuse and confusing

@Tony Delroy 2014-02-18 01:30:43

@SatyanRaina: please don't change the topic from incidental behaviours within the desirable API to fundamentally broken derivation. Specifically, your "is the order of inclusion..." and "...included/dropped based on their sequencial position..." tests are not good tests of the inappropriateness of a list, they just establish that a list has some unneeded properties. You suggest a HashSet which is sound advice, but using your logic I could say "Is it necessary to have O(1) lookup by key? No", "perhaps a performance test passes despite an inefficient algo". Testing is an arms race.

@Satyan Raina 2014-02-18 10:07:00

@TonyD I am not suggesting that the Player class should be derived from HashSet either. I am suggesting the Player class should 'in most cases' be implemented using a HashSet via Composition and that is totally an implementation level detail, not a design level one (that is why I mentioned it as a sidenote to my answer). It could very well be implemented using a list if there is a valid justification for such an implementation. So to answer your question, Is it necessary to have O(1) lookup by key? No. Therefore one should NOT extend a Player from a HashSet also.

@Tim B 2016-02-25 14:09:19

A set is considerably slower and has more overhead than an array or a list.You'd need to decide for each use-case whether that overhead is warranted.

@Mr Anderson 2016-09-01 19:40:28

You have mixed up parent-child relationship with inheritance. Completely different.

@Satyan Raina 2019-12-27 07:00:05

@MrAnderson Could you elaborate?

@xpmatteo 2014-02-11 16:51:21

It depends on the behaviour of your "team" object. If it behaves just like a collection, it might be OK to represent it first with a plain List. Then you might start to notice that you keep duplicating code that iterates on the list; at this point you have the option of creating a FootballTeam object that wraps the list of players. The FootballTeam class becomes the home for all the code that iterates on the list of players.

It makes my code needlessly verbose. I must now call my_team.Players.Count instead of just my_team.Count. Thankfully, with C# I can define indexers to make indexing transparent, and forward all the methods of the internal List... But that's a lot of code! What do I get for all that work?

Encapsulation. Your clients need not know what goes on inside of FootballTeam. For all your clients know, it might be implemented by looking the list of players up in a database. They don't need to know, and this improves your design.

It just plain doesn't make any sense. A football team doesn't "have" a list of players. It is the list of players. You don't say "John McFootballer has joined SomeTeam's players". You say "John has joined SomeTeam". You don't add a letter to "a string's characters", you add a letter to a string. You don't add a book to a library's books, you add a book to a library.

Exactly :) you will say footballTeam.Add(john), not footballTeam.List.Add(john). The internal list will not be visible.

@Mauro Sampietro 2014-12-22 08:32:28

OP wants to understand how to MODEL REALITY but then defines a football team as a list of football players (which is wrong conceptually). This is the problem. Any other argument is misleding to him in my opinion.

@xpmatteo 2014-12-23 12:20:41

I disagree that the list of players is wrong conceptually. Not necessarily. As someone else wrote in this page, "All models are wrong, but some are useful"

@Mauro Sampietro 2014-12-23 12:49:51

Right models are hard to get, once done they are the ones useful. If a model can be misused it is wrong conceptually.

@Mauro Sampietro 2014-02-11 16:25:06

A football team is not a list of football players. A football team is composed of a list of football players!

This is logically wrong:

class FootballTeam : List<FootballPlayer> 
    public string TeamName; 
    public int RunningTotal 

and this is correct:

class FootballTeam 
    public List<FootballPlayer> players
    public string TeamName; 
    public int RunningTotal 

@Servy 2014-02-11 16:48:44

This doesn't explain why. The OP knows that most other programmers feel this way, he just doesn't understand why it's important. This is only really providing information already in the question.

@rball 2014-02-11 17:11:59

This is the first thing I thought of too, how his data was just modeled incorrectly. So the reason why is, your data model is incorrect.

@Mauro Sampietro 2014-02-13 13:36:49

Why not inherit from list? Because he does not want a more specific kind of list.

@sara 2015-09-15 15:28:30

I don't think the second example is correct. You are exposing state and are thus breaking encapsulation. State should be hidden/internal to the object and the way to mutate this state should be via public methods. Exactly which methods is something to be determined from the business requirements, but it should be on a "need this right now"-basis, not "might be handy some time I dunno"-basis. Keep your api tight and you'll save yourself time and effort.

@Mauro Sampietro 2015-09-16 07:46:35

Encapsulation is not the point of the question. I focus on not extending a type if you don't want that type to behave in a more specific way. Those fields should be autoproperties in c# and get/set methods in other languages but here in this context that is totally superfluous

@Paul J Abernathy 2014-02-11 14:56:27

This reminds me of the "Is a" versus "has a" tradeoff. Sometimes it is easier and makesmore sense to inherit directly from a super class. Other times it makes more sense to create a standalone class and include the class you would have inherited from as a member variable. You can still access the functionality of the class but are not bound to the interface or any other constraints that might come from inheriting from the class.

Which do you do? As with a lot of depends on the context. The guide I would use is that in order to inherit from another class there truly should be an "is a" relationship. So if you a writing a class called BMW, it could inherit from Car because a BMW truly is a car. A Horse class can inherit from the Mammal class because a horse actually is a mammal in real life and any Mammal functionality should be relevant to Horse. But can you say that a team is a list? From what I can tell, it does not seem like a Team really "is a" List. So in this case, I would have a List as a member variable.

@Dmitry S. 2014-02-11 03:25:28

First of all, it has to do with usability. If you use inheritance, the Team class will expose behavior (methods) that are designed purely for object manipulation. For example, AsReadOnly() or CopyTo(obj) methods make no sense for the team object. Instead of the AddRange(items) method you would probably want a more descriptive AddPlayers(players) method.

If you want to use LINQ, implementing a generic interface such as ICollection<T> or IEnumerable<T> would make more sense.

As mentioned, composition is the right way to go about it. Just implement a list of players as a private variable.

Related Questions

Sponsored Content

43 Answered Questions

[SOLVED] How to make a flat list out of list of lists?

27 Answered Questions

[SOLVED] How do I check if a list is empty?

  • 2008-09-10 06:20:11
  • Ray
  • 3090688 View
  • 3233 Score
  • 27 Answer
  • Tags:   python list

32 Answered Questions

[SOLVED] Prefer composition over inheritance?

24 Answered Questions

[SOLVED] How do I concatenate two lists in Python?

31 Answered Questions

[SOLVED] Finding the index of an item in a list

  • 2008-10-07 01:39:38
  • Eugene M
  • 4090245 View
  • 3327 Score
  • 31 Answer
  • Tags:   python list indexing

22 Answered Questions

20 Answered Questions

6 Answered Questions

[SOLVED] Python class inherits object

10 Answered Questions

[SOLVED] Why is it string.join(list) instead of list.join(string)?

  • 2009-01-29 22:45:13
  • Evan Fosmark
  • 1300874 View
  • 1805 Score
  • 10 Answer
  • Tags:   python string list

Sponsored Content