Why not inherit from List?
Why not inherit from List?
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
(); 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 (anint
). .NET does not provide a class for storing football teams, so I will make my own class. The most similar and relevant existing structure isList
, so I will inherit from it:class FootballTeam : List
{ public string TeamName; public int RunningTotal }
But it turns out that a guideline says you shouldn't inherit from List
. 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 Players; }
There are two problems with this:
It makes my code needlessly verbose. I must now call
my_team.Players.Count
instead of justmy_team.Count
. Thankfully, with C# I can define indexers to make indexing transparent, and forward all the methods of the internalList
... But that's a lot of code! What do I get for all that work?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
always unacceptable? When is it acceptable? Why/why not? What must a programmer consider, when deciding whether to inherit from List
or not?
Answer by Simon Whitehead for Why not inherit from List?
Lastly, some suggest wrapping the List in something:
That is the correct way. "Needlessly wordy" is a bad way to look at this. It has an explicit meaning when you write my_team.Players.Count
. You want to count the players.
my_team.Count
..means nothing. Count what?
Your design is broken when you want to inherit from List
. Your team isn't a list.. it's an enclosing entity that owns other entities. A team owns players, so players should be part of it (a member).
If you're really worried about it being "needlessly wordy", you can always expose properties from the team:
public int PlayerCount { get { return Players.Count; } }
..which becomes:
my_team.PlayerCount
This has meaning now and adheres to The Law Of Demeter.
You should also consider adhering to the Composite reuse principle. By inheriting from List
, you're saying a team is a list of players and exposing needless methods out of it. This is incorrect - as you stated, a team is more than a list of players: it has a name, managers, board members, trainers, medical staff, salary caps, etc. By having your team class contain a list of players, you're saying "A team has a list of players", but it can also have other things.
Answer by Dmitry S. for Why not inherit from List?
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
or IEnumerable
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.
Answer by m-y for Why not inherit from List?
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
List
is highly optimized. It's 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
. Now it's as easy as doing: List > { ... } var list = new MyList
.(); - 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
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
very generic because it is a generic concept... the name says it all; it is just a collection. There are more precise versions such asSortedCollection
,ObservableCollection
,ReadOnlyCollection
, etc. each of which implementIList
but notList
. Collection
allows for members (i.e. Add, Remove, etc.) to be overridden because they are virtual.List
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 Inheritence. 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 _roster = new List(53); public IList 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. }
Answer by Eric Lippert for Why not inherit from List?
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
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
. And make it ReadOnlyList
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
always unacceptable?
Unacceptable to who? Me? No.
When is it acceptable?
When you're building a mechanism that extends the List
mechanism.
What must a programmer consider, when deciding whether to inherit from
List
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
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.
UPDATE
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.
Answer by stefan.schwetschke for Why not inherit from List?
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 a 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 footballTeam = ...
When using F#, it can even be OK to create a type abbreviation
type FootballTeam = IList
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, where it is not clear in which context it may be used in the future. 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.
Answer by Tim B for Why not inherit from List?
This is a classic example of composition vs inheritance.
In this specific case:
Is the team a list of players with added behavior
or
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:
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.
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.
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.
Answer by Paul J Abernathy for Why not inherit from List?
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 things...it 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.
Answer by Heidel Ber Gensis for Why not inherit from List?
class FootballTeam : List { 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:
Anyway, this code (from m-y's answer)
public class FootballTeam { // Football team rosters are generally 53 total players. private readonly List _roster = new List(53); public IList 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:
This is how is your logic presented in pictures...
Answer by sam for Why not inherit from List?
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 { public string TeamName; public int RunningTotal }
and this is correct:
class FootballTeam { public List players public string TeamName; public int RunningTotal }
Answer by xpmatteo for Why not inherit from List?
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.
Answer by Satyan Raina for Why not inherit from List?
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., http://en.wikipedia.org/wiki/Liskov_substitution_principle
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.
Answer by Arturo Tena for Why not inherit from List?
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.
Answer by Cruncher for Why not inherit from List?
Does allowing people to say
myTeam.subList(3, 5);
make any sense at all? If not then it shouldn't be a List.
Answer by El Zorko for Why not inherit from List?
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
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.
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, ICollection, ICollection { .... }
Design notwithstanding, at this point in C# you won't be able to implement all of these by inheriting from List
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.
Answer by Sam Leach for Why not inherit from List?
What if the FootballTeam
has a reserves team along with the main team?
class FootballTeam { List Players { get; set; } List ReservePlayers { get; set; } }
How would you model that with?
class FootballTeam : List { public string TeamName; public int RunningTotal }
The relationship is clearly has a and not is a.
or RetiredPlayers
?
class FootballTeam { List Players { get; set; } List ReservePlayers { get; set; } List 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
if you really wanted to do that.
Really List
is a perfectly good type to deal with. Maybe IList
if you are returning it from a method.
In summary
Ask yourself
Is
X
aY
? or HasX
aY
?Do my class names mean what they are?
Answer by Nicolas Dorier for Why not inherit from List?
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:
team.Players.ByName("Nicolas")
When I find it better than
team.ByName("Nicolas")
Moreover, my PlayerCollection might be used by other class, like "Club" without any code duplication.
club.Players.ByName("Nicolas")
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?
team.Players.ByName("Nicolas")
or
team.ByName("Nicolas")
Really. Do you have any doubt? Now maybe you need to play with other technical constraints that prevent you to use List
Answer by Craig Young for Why not inherit from List?
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.
Answer by ShitalShah for Why not inherit from List?
When they say List
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
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
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
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.
Answer by user1852503 for Why not inherit from List?
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.
Answer by Ivan Nikitin for Why not inherit from List?
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.
0 comments:
Post a Comment