Lors de la planification de mes programmes, je commence souvent par une chaîne de pensée comme celle-ci:
Une équipe de football n'est qu'une liste de joueurs de football. Par conséquent, je devrais le représenter avec:
var football_team = new List<FootballPlayer>();
L'ordre de cette liste représente l'ordre dans lequel les joueurs sont répertoriés dans la liste.
Mais je me rends compte plus tard que les équipes ont également d'autres propriétés, en plus de la simple liste de joueurs, qui doivent être enregistrées. Par exemple, le total cumulé des scores de cette saison, le budget actuel, les couleurs uniformes, un string
représentant le nom de l'équipe, etc.
Alors je pense:
D'accord, une équipe de football est comme une liste de joueurs, mais en plus, elle a un nom (a
string
) et un total cumulé de scores (anint
). .NET ne fournit pas de classe pour le stockage des équipes de football, je vais donc créer ma propre classe. La structure existante la plus similaire et la plus pertinente estList<FootballPlayer>
, donc j'en hériterai:class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
Mais il s'avère qu'une directive dit que vous ne devriez pas hériter deList<T>
. Je suis profondément confus par cette directive à deux égards.
Pourquoi pas?
Apparemment, il List
est en quelque sorte optimisé pour les performances . Comment? Quels problèmes de performances vais-je causer si je prolonge List
? Qu'est-ce qui se cassera exactement?
Une autre raison que j'ai vue est celle List
fournie par Microsoft, et je n'ai aucun contrôle dessus, donc je ne peux pas la changer plus tard, après avoir exposé une "API publique" . Mais j'ai du mal à comprendre cela. Qu'est-ce qu'une API publique et pourquoi devrais-je m'en soucier? Si mon projet actuel ne possède pas et n'est pas susceptible d'avoir cette API publique, puis-je ignorer cette directive en toute sécurité? Si j'hérite List
et qu'il s'avère que j'ai besoin d'une API publique, quelles difficultés aurai-je?
Pourquoi est-ce même important? Une liste est une liste. Qu'est-ce qui pourrait éventuellement changer? Que pourrais-je vouloir changer?
Et enfin, si Microsoft ne voulait pas que j'hérite List
, pourquoi n'ont-ils pas fait la classe sealed
?
Que dois-je utiliser d'autre?
Apparemment, pour les collections personnalisées, Microsoft a fourni une Collection
classe qui devrait être étendue au lieu de List
. Mais cette classe est très nue et n'a pas beaucoup de choses utiles, commeAddRange
par exemple. La réponse de jvitor83 fournit une justification des performances pour cette méthode particulière, mais comment un lent AddRange
n'est-il pas meilleur que non AddRange
?
Hériter de Collection
est bien plus de travail que d'hériter List
, et je ne vois aucun avantage. Certes, Microsoft ne me dirait pas de faire du travail supplémentaire sans raison, donc je ne peux m'empêcher de me sentir comme si je comprenais quelque chose, et hériter Collection
n'est en fait pas la bonne solution pour mon problème.
J'ai vu des suggestions telles que la mise en œuvre IList
. Tout simplement pas. Ce sont des dizaines de lignes de code passe-partout qui ne me rapportent rien.
Enfin, certains suggèrent d'envelopper List
quelque chose:
class FootballTeam
{
public List<FootballPlayer> Players;
}
Il y a deux problèmes avec ceci:
Cela rend mon code inutilement verbeux. Je dois maintenant appeler
my_team.Players.Count
au lieu de justemy_team.Count
. Heureusement, avec C # je peux définir des indexeurs pour rendre l'indexation transparente, et transmettre toutes les méthodes de l'interneList
... Mais c'est beaucoup de code! Qu'est-ce que j'obtiens pour tout ce travail?Cela n'a tout simplement aucun sens. Une équipe de football n'a pas de "liste" de joueurs. Il est la liste des joueurs. Vous ne dites pas "John McFootballer a rejoint les joueurs de SomeTeam". Vous dites "John a rejoint SomeTeam". Vous n'ajoutez pas de lettre aux "caractères d'une chaîne", vous ajoutez une lettre à une chaîne. Vous n'ajoutez pas un livre aux livres d'une bibliothèque, vous ajoutez un livre à une bibliothèque.
Je me rends compte que ce qui se passe "sous le capot" peut être considéré comme "l'ajout de X à la liste interne de Y", mais cela semble être une façon très contre-intuitive de penser le monde.
Ma question (résumée)
Quelle est la bonne façon de C # de représenter une structure de données, qui, « logiquement » (c'est - à - dire, « à l'esprit humain ») est juste list
de things
quelques cloches et de sifflets?
L'héritage est-il List<T>
toujours inacceptable? Quand est-ce acceptable? Pourquoi pourquoi pas? De quoi un programmeur doit-il tenir compte lorsqu'il décide d'hériter List<T>
ou non?
string
est requis pour faire tout ce qu'un object
peut faire et plus encore .