在计划我的程序时,我经常会从这样一个思路开始:
足球队只是足球运动员的名单。因此,我应该用以下方式表示它: cs var football_team = new List<FootballPlayer>(); 此列表的顺序代表球员在名册中列出的顺序。
足球队只是足球运动员的名单。因此,我应该用以下方式表示它:
cs var football_team = new List<FootballPlayer>();
此列表的顺序代表球员在名册中列出的顺序。
但我后来意识到,除了球员名单之外,球队还有其他属性必须记录下来。比如本赛季总成绩、当前预算、球衣颜色、string代表球队名称的a等。
string
那么我想:
好吧,一个足球队就像一个球员名单,但另外,它有一个名字 (a string) 和一个总得分 (an int)。.NET 没有提供用于存储足球队的类,所以我将创建自己的类。最相似和最相关的现有结构是List<FootballPlayer>,所以我将从它继承: cs class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
好吧,一个足球队就像一个球员名单,但另外,它有一个名字 (a string) 和一个总得分 (an int)。.NET 没有提供用于存储足球队的类,所以我将创建自己的类。最相似和最相关的现有结构是List<FootballPlayer>,所以我将从它继承:
int
List<FootballPlayer>
cs class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
但事实证明,一条准则说你不应该继承自List. 我在两个方面完全被这个指导方针弄糊涂了。
List
显然List以某种方式优化了性能。为何如此?如果我扩展会导致什么性能问题List?究竟会破坏什么?
我看到的另一个原因List是由 Microsoft 提供的,我无法控制它,所以在公开 “public API” 之后我无法更改它。但我很难理解这一点。什么是公共 API,我为什么要关心?如果我当前的项目没有也不太可能有这个公共 API,我可以安全地忽略这个指南吗?如果我确实继承自List 并且事实证明我需要一个公共 API,我会有什么困难?
为什么它甚至重要?一个列表就是一个列表。有什么可能改变?我可能想要改变什么?
最后,如果微软不想让我继承自List,他们为什么不创建这个类sealed?
sealed
显然,对于自定义集合,Microsoft 提供了一个Collection应该扩展的类,而不是List. 但是这个类很简单,没有很多有用的东西,比如AddRange,为该特定方法提供了性能原理,但是慢AddRange不比不好怎么AddRange办?
Collection
AddRange
继承 fromCollection比继承 from 要做更多的工作List,我认为没有任何好处。微软肯定不会无缘无故地告诉我做额外的工作,所以我不禁觉得我在某种程度上误解了一些东西,而继承Collection实际上并不是解决我问题的正确方法。
我已经看到了诸如实施之类的建议IList。就是不行。这是几十行样板代码,对我没有任何好处。
IList
最后,有些人建议将其包装List在一些东西中:
class FootballTeam { public List<FootballPlayer> Players; }
这有两个问题:
my_team.Players.Count
my_team.Count
我意识到“幕后”发生的事情可以说是“将 X 添加到 Y 的内部列表中”,但这似乎是一种非常违反直觉的思考世界的方式。
什么是表示数据结构的正确 C# 方式,“逻辑上”(也就是说,“对人的思想”)只是花里胡哨的一种list?things
list
things
继承自List<T>总是不可接受的吗?什么时候可以接受?为什么/为什么不?程序员在决定是否继承时必须考虑什么List<T>?
List<T>
这里有一些很好的答案。我会向他们补充以下几点。
什么是表示数据结构的正确 C# 方式,“逻辑上”(也就是说,“对于人类思维”)只是一个带有一些花里胡哨的东西的列表?
请任何十个熟悉足球存在的非计算机程序员来填空:
足球队是一种特殊的____
有人说“花里胡哨的足球运动员名单”,还是都说“运动队”或“俱乐部”或“组织”?您认为足球队是特定类型的球员名单的想法存在于您的人类思想中,并且仅存在于您的人类思想中。
List<T>是一种机制。足球队是一个业务对象,即表示程序业务领域中的某个概念的对象。不要混合这些!足球队是一种球队;它有一个花名册,一个花名册是一个球员名单。名册不是特定类型的球员名单。名册是球员的名单。因此,创建一个名为Rosterthat is a的属性List<Player>。并且ReadOnlyList<Player>当你在它的时候做它,除非你相信每个了解足球队的人都会从名单中删除球员。
Roster
List<Player>
ReadOnlyList<Player>
继承自List<T>总是不可接受的吗?
谁不能接受?我?不。
什么时候可以接受?
当您构建扩展机制的List<T>机制时。
程序员在决定是否继承时必须考虑什么List<T>?
我是在构建机制还是业务对象?
但这是很多代码!所有这些工作我能得到什么?
List<T>您花了更多时间输入您的问题,因为您需要为50 次以上的相关成员编写转发方法。您显然不怕冗长,我们在这里谈论的是非常少量的代码;这是几分钟的工作。
我对此进行了更多思考,还有另一个理由不将足球队建模为球员名单。事实上,将足球队建模为也有球员名单可能是个坏主意。球队作为/拥有球员名单的问题在于,你所拥有的是球队在某一时刻的快照。我不知道你对这门课的商业案例是什么,但如果我有一个代表足球队的课,我想问它“2003 年至 2013 年间有多少海鹰队球员因伤缺席比赛?”之类的问题。或“之前为另一支球队效力的丹佛球员中,哪位球员的码数同比增幅最大?” 或者“今年猪队一路走好吗? ”
也就是说,在我看来,一支足球队被很好地建模为历史事实的集合,例如球员何时被招募、受伤、退役等。显然,目前的球员名单是一个重要的事实,可能应该是前面和 -中心,但您可能还想对这个对象做一些其他有趣的事情,这些事情需要更多的历史视角。