c#泛型继承




为什么不从List<T>继承? (17)

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.

在规划我的节目时,我经常从一系列的想法开始,比如:

足球队只是一个足球运动员的名单。 因此,我应该用:

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#表示数据结构的方式,“逻辑上”(也就是说,“对于人类的头脑”)仅仅是things带有一些花里胡哨的thingslist

List<T>继承始终是不可接受的? 什么时候可以接受? 为什么/为什么不? 在决定是否继承List<T>时,程序员必须考虑什么?


最后,有人建议将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")

真。 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