为什么不从列表&LT继承; T>?不从、列表、GT、LT

2023-09-02 10:15:23 作者:夕颜〤

在规划出我的方案,我经常先从思想的链条,像这样:

  

一个足球队的足球运动员只是一个列表。所以,我应该重新用present它:

  VAR football_team =新的名单,其中,FootballPlayer>();
 
重磅 时间大变 企业6月30号前务必完成工商年报

     

这个名单重新present其中球员都列在名册顺序的排序。

不过,我后来才知道,球队也有其他属性,除了玩家单纯的名单,必须被记录下来。例如,正在运行的总得分,本赛季目前的预算,统一颜色,字符串重presenting球队的名字,等等。

于是我想:

  

好了,一支球队就像球员名单,但另外,它有一个名称(字符串)和一个正在运行的总分(一个 INT )。 .NET不用于存储足球队提供一个类,所以我会做我自己的类。最相似的,相关的现有结构名单,其中,FootballPlayer> ,所以我将继承它:

 类FootballTeam:列表< FootballPlayer>
{
    公共字符串TeamName;
    公众诠释runningTotal在
}
 

不过,事实证明,指导方针说,你不应该从名单,其中继承; T> 。我彻底被这个方针在两个方面感到困惑。

为什么不呢?

显然 列表是某种性能优化。怎么会这样?我会造成什么性能问题,如果我延长列表?究竟会打破?

另外一个原因,我所看到的是列表是由微软提供的,我没有控制权,所以的我以后不能暴露后更改它,。但我很难理解这一点。什么是公共API,我为什么要在乎?如果我当前的项目不和,也不太可能曾经有这样的公共API,我可以放心地忽略此指引?如果我从列表的和的事实证明,我需要一个公共的API,我会遇到什么困难?

继承

为什么它甚至关系?列表是一个列表。怎么可能改变?我能可能要更改?

最后,如果微软不想让我从列表继承,为什么他们不把类密封

还有什么我应该使用?

显然,对于自定义集合,微软已经提供了集合类,它应该扩展,而不是列表。但是,这个类是非常光秃秃的,而且没有很多有用的东西,如的AddRange ,例如。 jvitor83的回答为特定方法的性能原理,但如何是一个缓慢的的AddRange 不是比没有更好的的AddRange

系列继承是不是从列表继承的方式更多的工作,而我看不出有什么好处。当然,微软也不会告诉我做额外的工作没有任何理由,让我不禁感觉就像我在某种程度上误解的东西,秉承集合其实是不正确的解我的问题。

我见过的建议,如实施的IList 。就是不行。这是几十个样板code的斩获我什么线路。

最后,有人建议包裹列表的东西:

 类FootballTeam
{
    公开名单< FootballPlayer>玩家;
}
 

有两个问题:

这让我的code不必要的冗长。我现在必须调用 my_team.Players.Count ,而不是仅仅 my_team.Count 。值得庆幸的是,用C#我可以定义索引,使索引透明,并转发内部列表中的所有方法 ...但是,这是一个很大的code!我能得到什么了所有这些工作?

这只是普通的没有任何意义。一支足球队没有有的球员名单。它的是的球员名单。你不说约翰McFootballer加入SomeTeam的球员。你说约翰已经加入SomeTeam。你不添加到字符串的字符,你信添加到字符串的信。你不添加一本书到一个图书馆的书籍,你添加一本书到库中。

我知道会发生什么引擎盖下,可以说是增加X到Y的内部名单,但是这似乎是思考世界的一个非常反直觉的方式。

我的问题(摘要)

什么是正确的C#的方式重新presenting的数据结构,其中,逻辑(即以人的心灵)仅仅是一个列表 有一些花俏?

继承名单,其中,T> 总是不能接受?什么时候可以接受的?为什么/为什么不呢?什么是必须程序员考虑,决定是否从名单,其中继承时,T> 或不

解决方案

这里有一些很好的答案。我想补充他们以下几点。

  

什么是正确的C#的方式重新presenting的数据结构,其中,逻辑(即以人的心灵)是物联网只是有一些花俏的列表?

问任何十个非计算机程序员的人谁是熟悉足球的存在,填补了空白:

  A足球队是一种特殊的_____
 

难道的任何的说:足球运动员与一些花里胡哨名单,还是他们都表示运动队或俱乐部或组织?你的概念,即一个足球队的有一种特殊的球员名单的是在人的心灵和你的人的心灵孤独。

名单,其中,T> 是机制的。足球队是一个的业务对象的 - 也就是重新presents一些概念,是在商业领域的程序的对象。不要混淆这些!一支足球队的是一种的团队;它的有的名册,名册的是球员名单的。名册不是的特殊类型的球员名单的。名册的是的玩家名单。因此,让一个名为属性名册这是一个名单,其中,播放器> 。并使其 ReadOnlyList<播放器> ,而你在它,除非你相信大家谁知道一个足球队获得从名册上删除的球员

  

继承名单,其中,T> 总是不能接受?

不能接受的是谁?我?没有。

  

当是可以接受的?

当你正在构建的的机制扩展了名单,其中,T> 机制的

  

什么是必须程序员考虑,决定何时是否从继承名单,其中,T> 或不

我是不是建设的机制的或的业务对象的?

  

但是,这是一个很大的code!我能得到什么了所有这些工作?

您花更多的时间打字了你的问题,它会采取你写转发方法有关成员名单,其中,T> 五十次以上。你显然不害怕冗长的,我们正在谈论的是一个非常小的金额code在这里;这是几分钟工作

更新

我给它一些更多的思考和还有另外一个原因,以一支足球队不是模型作为球员名单。事实上,它可能是一个坏主意,一个足球队建模为的为的一个球员名单了。与一队的/有球员名单的问题是,你所得到的是一个的快照的球队的在时刻的的。我不知道你的商业案例就是这个类,但如果我有一个类,再presented足球队我想问这问题,如有多少海鹰球员错过了因伤病游戏2003之间2013?或者什么丹佛球员谁previously效力于其他球队有最大的一年,比去年增加了码跑了?或做了Piggers一路走下去今年呢?

这就是一支球队在我看来,有良好的建模为的的历史事实的集合的,当一个球员被征召,受伤,退休等,如显然目前的球员名册是重要的事实,也许应该是前面和中心,但也有可能是你想要做那些需要更多的历史的角度来看此对象的其它有趣的东西。

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:

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?

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<T> always unacceptable? When is it acceptable? Why/why not? What must a programmer consider, when deciding whether to inherit from List<T> or not?

解决方案

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<T> 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<Player>. And make it ReadOnlyList<Player> 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<T> always unacceptable?

Unacceptable to who? Me? No.

When is it acceptable?

When you're building a mechanism that extends the List<T> mechanism.

What must a programmer consider, when deciding whether to inherit from List<T> 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<T> 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.