C#中为什么不能继承List?

jopen 10年前

问题:

每当构思我的程序时,我经常会有以下一连串的思考:

一个足球队是一群足球运动员的集合,因此,我可以用下面的代码来声明:

var football_team = new List<FootballPlayer>();

这个list的排序顺序就是这些运动员在花名册里的顺序。但是之后我又意识到除了运动员外,球队还有别的属性也需要记录。比如说这个赛季的总得分,当前的预算,球服的颜色,用string类型表示的球队名字,等等。

所以我认为:

既然一个球队就是一群运动员的集合,但是额外的,它有一个名字(string类型)和一个当前总得分(int类型)。 .NET没有提供一个用来存储球队的类,所以我得自己定义一个类。和现有结构最接近的是List<FootballPlayer>,所以我将继承它:

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

但是结果是,我被提示不能继承List<T>。 关于这个提示,我有两点疑惑:

为什么不能?

显然List某种程度是对性能的优化,怎么做到的?如果我继承了List对性能会有什么影响?到底带来了什么?

另外,List是微软提供的,我不能控制它,当它对外界表征为“public API”我就不能修改它。但是我很难理解什么是Public API,为什么我要关心它?如果我的项目不需要或者根本没有这样的public API,我是不是就可以无视那个提示?如果我不继承List那么我便需要一个Public API,我该怎么解决?为什么它这么重要呢,一个list就是个list,有什么可能改变的呢?我可能想要改变什么呢?

最后,如果微软不希望我继承List,为什么不给List类加sealed修饰符呢?

除此之外我该使用什么?

显然,对于自定义的集合,微软提供了可以继承的Collection类来取代List。但是这个类功能很少,没有提供很多有用的东西,比如说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已经加入了某个球队的运动员集合里,你会说John McFootballer加入了某个球队。你不会说把一个字母加入到一串字符串的字母集合里,你会说把一个字符加入一串字符串里。你不会说把一本书放到一个图书馆的图书集合里,你会说把一本书放到一个图书馆里。

我开始意识到为什么会这样了,其实可以说成“把x添加到y的内部集合里”,但是这样的方式思考世界似乎是违背常理的。

我的问题(总结)

用C#表达逻辑(也就是说,从人类的思考方式)是list之类的带有钟声和口哨的数据结构,正确的方法是什么?

是不是继承List<T>一直不会被接受?或者说什么时候可以?为什么能或不能?当决定是否继承List<T>,一个程序员需要思考什么?

 

 

最佳答案:(来自 Eric Lippert,他是微软的一名资深软件设计工程师,之前伯乐在线翻译过他的一些博文

已经有很多很好的答案,我很乐意在他们的基础上补充几点。

用C#表达逻辑(也就说说,从人类的思考方式)是list之类的带有钟声和口哨的数据结构,正确的方法是什么?

让任意十个非计算机程序员并且熟悉足球的人来填写下面这句话的空白处:

一个足球队是一种特定的__________

有任何一个人写“带有钟声和口哨的运动员集合”吗,或者还是都写“运动队”、“俱乐部”、“组织”?足球队是一种特定的运动员集合这个概念只是你大脑中的想法并且也只是你而已。

List<T>是一种机制。足球队是一种商业对象——也就是说,在程序中,这种对象是在业务逻辑领域中的一种概念。不要混淆了这些。足球队就是一个团队,它有一个花名册,花名册是一组运动员集合。它就是运动员的集合。所以定义一个属性叫Roster表示List<Player>,并且当你使用时,定义成

ReadOnly List<Player>

除非你认为每个人都可以得到球队的花名册并且可以从中删除队员。

是不是继承List<T>一直不会被接受?

不能被谁接受?我吗?不是。

什么时候可以?

当你需要创建一种需要继承List<T>机制的机制的时候。

当决定是否继承List<T>,一个程序员需要思考什么?

我是在创建一种机制还是创建一个业务对象?

我要写很多代码。做那么多的活我得到了什么?

你花了大量的时间把你的问题敲打出来,这个时间已经够你把与List<T>相关联的成员方法写上50遍以上了。显然你是不怕冗长的,我们在这里讨论的只是几行代码,这是几分钟的工作。

更新

我又思考了一下,还有一个理由来说明足球队不是运动员的集合。事实上,觉得足球队包含一个运动员集合也许也是一个不对的想法。不管是球队是运动员集合还是有一个运动员集合,问题是你得到的只是球队某个时刻的一个快照,我不知道你定义这个类的业务情景式什么,但是如果让我来定义一个表示足球队的类,我会先问自己,“多少西雅图海鹰队的队员由于伤病错过了2003~2013年之间的比赛 ?”或“哪一个先前在另一个队效力的丹佛队运动员拥有在码跑中的最高年增长率?”或“今年Piggers 还会一路走好吗?”

也就是说,对我来说一个足球队就是一个对球员们雇佣、伤病、退役等生涯进行记录的记录集。很明显,当前的花名册就是一个重要的记录,所以应该是首先要考虑的,但是对足球队这个对象来说,也许你有更多别的你感兴趣东西,但那需要有更高的历史视角。

原文链接: stackoverflow   翻译: 伯乐在线 - 闪了腰的企鹅
译文链接: http://blog.jobbole.com/62371/