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

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

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

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; 
}

这有两个问题:

  • 它使我的代码不必要地冗长。 我现在必须调用my_team.Players.Count而不仅仅是my_team.Count 。 值得庆幸的是,在C#中,我可以定义索引器来使索引变得透明,并且转发内部List所有方法......但是这是很多代码! 我为这些工作得到什么?

  • 它只是普通的没有任何意义。 一个足球队没有“拥有”一个球员名单。 这是球员的名单。 你不会说“John McFootballer加入了SomeTeam的球员”。 你说“约翰加入了SomeTeam”。 您不会为“字符串的字符”添加字母,而是将字母添加到字符串中。 你不会为图书馆的书籍添加书籍,而是向图书馆添加一本书。

  • 我意识到,“引擎盖下”发生的事情可以说是“在X的内部列表中添加X”,但这似乎是一种非常直观的思考世界的方式。

    我的问题(总结)

    什么是代表一个数据结构,正确的C#的方式,其“逻辑”(即,“以人的心灵”)只是一个listthings有一些花俏?

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


    这里有一些很好的答案。 我会给他们添加以下几点。

    什么是正确的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年期间有多少海鹰队球员因伤缺席比赛?” 或者“之前效力于另一支球队的丹佛球员的场均跑步数量同比增幅最大?” 或者“这些猪人今年一路走来了吗?”

    也就是说,足球队在我看来很好的模仿了一系列历史事实,例如当一名球员被招募,受伤,退役等等。显然,当前的球员名单是一个重要的事实,中心,但可能还有其他有趣的事情需要用更多的历史视角来处理。


    最后,有人建议将List包装在一些东西中:

    这是正确的方法。 “不必要的罗嗦”是一个不好的方式来看待这一点。 它在写my_team.Players.Count时有明确的含义。 你想要统计球员。

    my_team.Count
    

    没有任何意义。 算什么?

    一个团队不是一个清单 - 不仅仅是一个球员列表。 一个球队拥有球员,所以球员应该成为球员的一部分(一名成员)。

    如果你真的担心它过于冗长,你可以随时暴露团队的属性:

    public int PlayerCount {
        get {
            return Players.Count;
        }
    }
    

    成为:

    my_team.PlayerCount
    

    这现在有意义并遵守得墨忒耳定律。

    您还应该考虑遵守复合重用原则。 通过从List<T>继承,你说的是一个团队是一个球员列表,并暴露出不必要的方法。 这是不正确的 - 正如你所说的,一个团队不仅仅是一个球员名单:它有一个名字,经理,董事会成员,培训师,医务人员,工资帽等等。通过让你的团队级别包含一个球员名单,你'说一支球队有一份球员名单',但它也可以有其他的东西。


    哇,你的文章有很多问题和观点。 你从微软获得的大部分推理都是正确的。 让我们从关于List<T>一切开始

  • List<T> 高度优化。 它的主要用途是用作对象的私有成员。
  • 微软并没有对此加以密封,因为有时你可能想创建一个具有友好名称的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.
    }
    
    链接地址: http://www.djcxy.com/p/1577.html

    上一篇: Why not inherit from List<T>?

    下一篇: What do two question marks together mean in C#?