Why not inherit from List ? Ask Question

Why not inherit from List ? Ask Question

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. コードが不必要に冗長になります。my_team.Players.Countだけではなく を呼び出す必要がありますmy_team.Count。ありがたいことに、C# ではインデックス作成を透過的にするインデクサーを定義して、内部のすべてのメソッドを転送できますList... しかし、それは大量のコードです。そのすべての作業で何が得られるのでしょうか?

  2. まったく意味をなさない。フットボール チームには選手のリストが「ある」のではない。それは選手のリストなのだ。「John McFootballer が SomeTeam の選手に加わった」とは言わない。「John が SomeTeam に加わった」と言うのだ。「文字列の文字」に文字を追加するのではなく、文字列に文字を追加するのだ。図書館の本に本を追加するのではなく、図書館に本を追加するのだ。

「内部で」何が起こっているかは「X を Y の内部リストに追加する」と言えることは理解していますが、これは世界についての非常に直感に反した考え方のように思えます。

私の質問(要約)

「論理的には」(つまり「人間の心にとって」) 単なるいくつかの付加機能を備えた であるデータ構造を表現する正しい C# の方法は何listですかthings?

継承はList<T>常に受け入れられないのでしょうか? いつ受け入れられるのでしょうか? その理由は? 継承するかどうList<T>かを決定する際に、プログラマーが考慮しなければならないことは何でしょうか?

ベストアンサー1

ここには良い回答がいくつかあります。私はそれに以下の点を追加したいと思います。

「論理的に」(つまり、「人間の心にとって」) いくつかの付加機能が付いた単なるリストであるデータ構造を表現する正しい C# の方法は何ですか?

サッカーの存在に詳しい、コンピュータプログラマー以外の 10 人に、空欄を埋めてもらいます。

サッカーチームは特別な種類の_____である

「ちょっとした装飾のあるサッカー選手のリスト」と言った人はいましたか、それとも全員が「スポーツチーム」や「クラブ」や「組織」と言ったのでしょうか。サッカーチームが特定の種類の選手のリストであるというあなたの考えは、あなたの人間の心の中にだけ存在しています。

List<T>はメカニズムです。フットボール チームはビジネス オブジェクト、つまり、プログラムのビジネス ドメインにある概念を表すオブジェクトです。これらを混同しないでください。フットボール チームは一種のチームです。チームには名簿があり、名簿は選手のリストです。名簿は特定の種類の選手のリストではありません。名簿は選手のリストですRoster。したがって、というプロパティを作成します。フットボール チームについて知っている人全員が名簿から選手を削除できると信じていない限り、ついでに もList<Player>作成します。ReadOnlyList<Player>

継承はList<T>常に受け入れられないのでしょうか?

誰にとって受け入れられないのか?私?いいえ。

いつなら受け入れられますか?

メカニズムを拡張するList<T>メカニズムを構築する場合

継承するかどうかを決定する際に、プログラマーは何を考慮する必要がありますList<T>か?

メカニズムを構築しているのか、それともビジネス オブジェクトを構築しているのか?

しかし、それは大量のコードです。そのすべての作業に対して何が得られるのでしょうか?

質問を入力するのにかかった時間は、関連するメンバーの転送メソッドをList<T>50 回以上記述するのにかかった時間よりも長かったでしょう。あなたは明らかに冗長さを恐れていないようです。ここで話しているのは非常に少量のコードです。これは数分でできる作業です。

アップデート

もう少し考えてみたところ、フットボール チームを選手のリストとしてモデル化しない別の理由があることがわかりました。実際、フットボール チームを選手のリストとしてモデル化するのはよくない考えかもしれません。チームを選手のリストとして/リストとして持つことの問題点は、得られるのはある瞬間のチームのスナップショットだということです。このクラスのビジネス ケースが何なのかはわかりませんが、フットボール チームを表すクラスがあったら、「2003 年から 2013 年の間に怪我で試合を欠場したシーホークスの選手は何人いますか?」や「以前別のチームでプレーしていたデンバーの選手のうち、前年比で走ったヤード数が最も増加したのは誰ですか?」などの質問をしたいと思います。ピガーズは今年も最後まで勝ち進みましたか?

つまり、フットボール チームは、選手がいつ採用されたか、負傷したか、引退したかなどの歴史的事実の集合として適切にモデル化されているように私には思えます。現在の選手名簿は明らかに重要な事実であり、おそらく最前線に置くべきものですが、このオブジェクトを使用して、より歴史的な観点を必要とする他の興味深いことを実行したい場合もあるでしょう。

おすすめ記事