c# inheritance tutorial




為什麼不從List<T>繼承? (17)

在規劃我的節目時,我經常從一系列的想法開始,比如:

足球隊只是一個足球運動員的名單。 因此,我應該用:

var football_team = new List<FootballPlayer>();

此列表的排序表示球員列入名單的順序。

但我後來認識到,除了僅僅是球員名單之外,球隊還有其他屬性,必須記錄下來。 例如,本賽季得分總數,當前預算,統一顏色,代表球隊名稱的string等。

那麼我想:

好吧,足球隊就像一個球員列表,但另外它有一個名字(一個string )和一個總分數(一個int )。 .NET不提供存儲橄欖球隊的類,所以我會自己創建類。 最相似和相關的現有結構是List<FootballPlayer> ,所以我將繼承它:

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

但事實證明, 指南指出你不應該繼承List<T> 。 本指南在兩方面讓我感到十分困惑。

為什麼不?

顯然, List以某種方式優化了性能 。 怎麼會這樣? 如果我擴展List會導致哪些性能問題? 什麼會打破?

我看到的另一個原因是List是由Microsoft提供的,我無法控制它,因此在公開“公共API”後我不能在以後更改它 。 但我很難理解這一點。 什麼是公共API,我為什麼要關心? 如果我目前的項目沒有,也不可能擁有這個公共API,我可以放心地忽略這個指南嗎? 如果我從List繼承,事實證明我需要一個公共API,那麼我會遇到什麼困難?

為什麼它甚至重要? 列表是一個列表。 什麼可能改變? 我可能想要改變什麼?

最後,如果微軟不希望我繼承List ,為什麼他們沒有把課程sealed

我還應該使用什麼?

顯然,對於自定義集合,Microsoft提供了一個應該擴展而不是ListCollection類。 但是這個類是非常AddRange ,並沒有很多有用的東西, 比如AddRange 。 jvitor83的答案提供了該特定方法的性能基本原理,但是如何緩慢的AddRange不比沒有AddRange更好?

Collection繼承比繼承List ,我看不到任何好處。 毫無疑問,微軟不會讓我無緣無故地做額外的工作,所以我不禁感覺自己在某種程度上誤解了某些東西,並且繼承Collection實際上並不是我的問題的正確解決方案。

我見過諸如實施IList建議。 就是不行。 這是幾十行樣板代碼,沒有任何收穫。

最後,有人建議將List包裝在一些東西中:

class FootballTeam 
{ 
    public List<FootballPlayer> Players; 
}

這有兩個問題:

  1. 它使我的代碼不必要地冗長。 我現在必須調用my_team.Players.Count而不僅僅是my_team.Count 。 值得慶幸的是,在C#中,我可以定義索引器來使索引變得透明,並且轉發內部List所有方法......但是這是很多代碼! 我為這些工作得到什麼?

  2. 它只是普通的沒有任何意義。 一個足球隊沒有“擁有”一個球員名單。 這球員的名單。 你不會說“John McFootballer加入了SomeTeam的球員”。 你說“約翰加入了SomeTeam”。 您不會為“字符串的字符”添加字母,而是將字母添加到字符串中。 你不會為圖書館的書籍添加書籍,而是向圖書館添加一本書。

我意識到,“引擎蓋下”發生的事情可以說是“在X的內部列表中添加X”,但這似乎是一種非常直觀的思考世界的方式。

我的問題(總結)

什麼是正確的C#表示數據結構的方式,“邏輯地”(即,“對人類的頭腦”)只是thingslist和幾個花里胡哨的?

List<T>繼承始終是不可接受的? 什麼時候可以接受? 為什麼/為什麼不? 在決定是否繼承List<T>時,程序員必須考慮什麼?


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

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

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

Choose one that is useful to you and/your users. 而已。 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.


最後,有人建議將List包裝在一些東西中:

這是正確的方法。 “不必要的羅嗦”是看這個不好的方法。 它在寫my_team.Players.Count時有明確的含義。 你想要統計球員。

my_team.Count

沒有任何意義。 算什麼?

一個團隊不是一個清單 - 不僅僅是一個球員列表。 一個球隊擁有球員,所以球員應該成為球員的一部分(一名成員)。

如果你真的擔心它過於冗長,你可以隨時暴露團隊的屬性:

public int PlayerCount {
    get {
        return Players.Count;
    }
}

成為:

my_team.PlayerCount

這現在有意義並遵守得墨忒耳定律

您還應該考慮遵守複合重用原則 。 通過從List<T>繼承,你說的是一個團隊是一個球員列表,並暴露出不必要的方法。 這是不正確的 - 正如你所說的,一個團隊不僅僅是一個球員名單:它有一個名字,經理,董事會成員,教練,醫務人員,工資帽等等。通過讓你的團隊級別包含一個球員名單,你'說一支球隊有一份球員名單',但它也可以有其他的東西。


設計>實施

你公開哪些方法和屬性是設計決定。 您繼承的基類是實現細節。 我覺得值得回到前者。

一個對像是數據和行為的集合。

所以你的第一個問題應該是:

  • 這個對像在我創建的模型中包含哪些數據?
  • 該對像在該模型中展現了什麼行為?
  • 未來如何改變?

請記住,繼承意味著一個“isa”(是)關係,而組合意味著“有一個”(hasa)關係。 根據您的觀點選擇適合您情況的產品,並考慮應用程序發展時的情況。

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>, 
                         ICollection<StaffMember>,
                         ICollection<Score>
{
    ....
}

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.


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 
}

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.


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();
}

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.


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> . 為什麼不?

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.


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") 

要么

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


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; 例如:

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.


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.


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.


哇,你的文章有很多問題和觀點。 你從微軟獲得的大部分推理都是正確的。 讓我們從關於List<T>一切開始

  • List<T>高度優化。 它的主要用途是用作對象的私有成員。
  • 微軟並沒有給它class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... } ,因為有時你可能想創建一個具有友好名稱的class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... }class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... } 。 現在就像做var list = new MyList<int, string>();
  • CA1002:不要公開通用名單 :基本上,即使你打算使用這個應用程序作為唯一的開發者,也應該用良好的編碼實踐進行開發,這樣他們才能灌輸到你的身上和第二性質。 如果您需要任何消費者擁有索引列表,您仍然可以將列表公開為IList<T> 。 這讓你在稍後改變類的實現。
  • 微軟使Collection<T>非常通用,因為它是一個通用的概念......名字說明了一切; 它只是一個集合。 有更精確的版本,如SortedCollection<T>ObservableCollection<T>ReadOnlyCollection<T>等,每個實現IList<T>不是 List<T>
  • Collection<T>允許成員(即添加,刪除等)被覆蓋,因為它們是虛擬的。 List<T>不。
  • 你問題的最後部分是現貨。 足球隊不僅僅是一個球員列表,所以它應該是一個包含球員列表的類。 構思與繼承 。 足球隊一個球員名單(名冊),這不是一個球員名單。

如果我寫這段代碼,這個類可能看起來像這樣:

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

如果FootballTeam主隊有一支儲備球隊會怎麼樣?

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

你將如何模擬?

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

這種關係顯然有一個而不是一個

RetiredPlayers

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

作為一個經驗法則,如果您想要從集合繼承,請命名類SomethingCollection

你的SomethingCollection語義上有意義嗎? 只有在你的類型 Something 集合時才這樣做。

FootballTeam的情況下,這聽起來不對。 一個Team不僅僅是一個Collection 。 其他答案指出, Team可以有教練,教練等。

FootballCollection聽起來像是一系列足球或者足球用品的集合。 TeamCollection ,一組團隊。

FootballPlayerCollection聽起來像是一個球員集合,如果你真的想這麼做的話,這將是一個從List<FootballPlayer>繼承的類的有效名稱。

真正List<FootballPlayer>是一個非常好的類型來處理。 也許IList<FootballPlayer>如果你從一個方法返回它。

綜上所述

問你自己

  1. XY ? 或 X Y

  2. 我的班級名稱是什麼意思?


這是compositioninheritance的典型例子。

在這個特定的情況下:

球隊是否有增加行為的球員名單?

要么

球隊是否屬於自己的對象,恰好包含了球員列表。

通過擴展List,您可以通過多種方式來限制自己:

  1. 您不能限制訪問權限(例如,阻止人員更改名單)。 無論您是否需要/需要它們,您都可以獲得所有List方法。

  2. 如果您想要列出其他內容,會發生什麼情況。 例如,球隊有教練,經理,球迷,裝備等。其中一些很可能是他們自己的名單。

  3. 你限制你的繼承選項。 例如,您可能想要創建一個通用的Team對象,然後讓BaseballTeam,FootballTeam等從中繼承。 要從List繼承,您需要從Team繼承,但這意味著所有不同類型的團隊都被迫擁有與該名單相同的實現。

構圖 - 包括一個對象,在對像中給出你想要的行為。

繼承 - 您的對象成為具有所需行為的對象的實例。

兩者都有其用途,但這是一個明顯的情況,其中組合物是優選的。


這裡有一些很好的答案。 我會給他們添加以下幾點。

什麼是正確的C#表示數據結構的方式,“邏輯地”(即,“對人類的頭腦”)只是一些事情的列表和幾個花里胡哨的?

請任何十位熟悉足球存在的非計算機程序員的人填寫空白處:

A football team is a particular kind of _____

有沒有說過“有幾個足球運動員名單”,還是他們都說“運動隊”,“俱樂部”或“組織”? 你的觀點,即足球隊是一種特殊的球員列表,只有你的人類思想和人類的思想。

List<T>是一個機制 。 足球隊是一個業務對象 - 也就是說,代表某個概念的對像在程序的業務領域 。 不要混合這些! 足球隊是一種團隊; 它有一個名冊,一個名冊是一個球員名單 。 名冊不是一種特殊的球員列表 。 名單一個球員名單。 因此,創建一個名為Roster的屬性是一個List<Player> 。 並且在你看到它的時候使它成為ReadOnlyList<Player> ,除非你相信每個了解足球隊的人都會從名單中刪除球員。

List<T>繼承始終是不可接受的?

誰不能接受? 我? 沒有。

什麼時候可以接受?

在構建擴展List<T>機制的機制時

在決定是否繼承List<T>時,程序員必須考慮什麼?

我是在建立一個機制還是一個商業對象

但是這是很多代碼! 我為這些工作得到什麼?

你花了更多的時間來輸入你的問題,它會讓你為List<T>的相關成員編寫50次轉發方法。 你顯然不怕冗長,而且我們在這裡談論的是非常少量的代碼; 這是幾分鐘的工作。

UPDATE

我給了它更多的思考,還有另一個理由不模仿一個足球隊作為球員名單。 事實上,將一個橄欖球隊建模成球員名單也許是一個不好的主意。 球隊/球員名單的問題在於,你所得到的是球隊在某個時刻快照 。 我不知道你們這個班的商業案例是什麼,但是如果我有一個代表足球隊的課程,我想問一些問題,比如“在2003年到2013年期間有多少海鷹隊球員因傷缺席比賽?” 或者“之前曾效力於另一支球隊的丹佛球員的年度同比漲幅最大嗎?” 或者“ 這些豬人今年一路走來了嗎?

也就是說,足球隊在我看來很好的模仿了一系列歷史事實,例如當一名球員被招募,受傷,退役等等。顯然,當前的球員名單是一個重要的事實,中心,但可能還有其他有趣的事情需要用更多的歷史視角來處理。


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

以前的代碼意味著:一群來自街頭踢球的球員,他們恰好有一個名字。 就像是:

無論如何,這個代碼(從我的答案)

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

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

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

意思是:這是一個擁有管理,球員,管理員等的足球隊。例如:

這是你的邏輯如何呈現在圖片中......





design