在规划我的程序时,我经常从这样的一连串想法开始:
一个足球队只是一个足球运动员的名单。因此,我应该代表它:
var football_team = new List<FootballPlayer>();
此列表的顺序表示球员在名册中列出的顺序。
但我后来意识到,除了球员名单之外,球队还有其他必须记录的属性。例如,本赛季的总比分、当前预算、统一的颜色、代表球队名称的string
等。
于是我想:
好的,足球队就像一个球员列表,但除此之外,它还有一个名字(a
string
)和一个持续的总得分(aint
)。. NET没有提供用于存储足球队的类,所以我将制作自己的类。最相似和最相关的现有结构是List<FootballPlayer>
,所以我将从它继承:class FootballTeam : List<FootballPlayer>{public string TeamName;public int RunningTotal}
但事实证明,指南说你不应该从#0继承。我对这个指导方针在两个方面感到非常困惑。
显然#0在某种程度上优化了性能。为什么?如果我扩展List
,会导致什么性能问题?到底会破坏什么?
我看到的另一个原因是List
是微软提供的,我无法控制它,所以在暴露了一个“公共API”之后,我无法稍后更改它。但我很难理解这一点。什么是公共API,我为什么要关心?如果我当前的项目没有也不可能有这个公共API,我可以放心地忽略这一准则吗?如果我继承了List
和,事实证明我需要一个公共API,我会有什么困难?
这有什么关系?列表就是列表。有什么可能改变?我可能想改变什么?
最后,如果微软不希望我从List
继承,为什么他们不做类sealed
?
显然,对于自定义集合,Microsoft提供了一个Collection
类,它应该扩展而不是List
。但是这个类非常简单,并且没有很多有用的东西,例如如#2。jvitor83的答案为该特定方法提供了性能原理,但是缓慢的AddRange
为什么不比没有AddRange
好呢?
从Collection
继承比从List
继承要多得多,我看不到任何好处。当然,微软不会无缘无故地让我做额外的工作,所以我不禁觉得我在某种程度上误解了什么,继承Collection
实际上不是我问题的正确解决方案。
我已经看到了实现IList
之类的建议。只是没有。这是几十行样板代码,对我没有任何好处。
最后,有些人建议用某种东西包装List
:
class FootballTeam{public List<FootballPlayer> Players;}
这有两个问题:
它使我的代码不必要地冗长。我现在必须调用my_team.Players.Count
而不仅仅是my_team.Count
。幸运的是,使用C#我可以定义索引器以使索引透明,并转发内部List
的所有方法……但这是很多代码!我从所有这些工作中得到了什么?
它只是没有任何意义。一个足球队没有“有”一个球员名单。它是球员名单。你不会说“约翰·麦克足球先生加入了某个团队的球员”。你会说“约翰加入了某个团队”。你不会在“字符串的字符”中添加一个字母,你会在字符串中添加一个字母。你不会把一本书添加到图书馆的书中,你会把一本书添加到图书馆。
我意识到“引擎盖下”发生的事情可以说是“将X添加到Y的内部列表中”,但这似乎是一种非常违反直觉的思考世界的方式。
什么是表示数据结构的正确C#方式,“逻辑上”(也就是说,“对人类思维”)只是things
中的list
,只有一些花里胡哨的东西?
从List<T>
继承总是不可接受的吗?什么时候可以接受?为什么/为什么不?程序员在决定是否从List<T>
继承时必须考虑什么?