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

标签 c# .net list oop inheritance

在规划我的程序时,我经常以这样的思路开始:

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.



但后来我意识到,除了球员名单之外,球队还有其他一些必须记录的属性。比如本赛季跑分总分,当前预算,统一颜色,一个string代表队名等。

那么我想:

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 
}


但事实证明,a guideline says you shouldn't inherit from List<T> .我对这个指南在两个方面感到非常困惑。

为什么不?

显然 List is somehow optimized for performance .为何如此?如果我扩展 List 会导致什么性能问题?究竟会破什么?

我见过的另一个原因是 List由微软提供,我无法控制,所以 I cannot change it later, after exposing a "public API" .但我很难理解这一点。什么是公共(public) API,我为什么要关心?如果我当前的项目没有也不太可能有这个公共(public) API,我可以安全地忽略这个指南吗?如果我确实继承自 List事实证明我需要一个公共(public) API,我会有什么困难?

为什么这很重要?列表是一个列表。什么可能改变?我可能想要改变什么?

最后,如果微软不想让我继承 List ,他们为什么不上课sealed ?

我还应该使用什么?

显然,对于自定义集合,Microsoft 提供了一个 Collection应该扩展的类而不是 List .但是这个类很空,有用的东西不多,such as AddRange , 例如。 jvitor83's answer为该特定方法提供了性能原理,但速度如何慢 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 的球员”。您说“John 已加入 SomeTeam”。您不会向“字符串的字符”添加字母,而是向字符串添加字母。您不是将一本书添加到图书馆的书籍中,而是将一本书添加到图书馆。

  • 我意识到“幕后”发生的事情可以说是“将 X 添加到 Y 的内部列表中”,但这似乎是一种非常反直觉的思考世界的方式。

    我的问题(总结)

    表示数据结构的正确 C# 方式是什么,“逻辑上”(也就是说,“对人的思想”)只是一个 listthings有一些花里胡哨?

    继承自 List<T>总是不能接受?什么时候可以接受?为什么/为什么不?程序员在决定是否从 List<T> 继承时必须考虑什么或不?

    最佳答案

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

    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?



    请任何十名熟悉足球存在的非计算机程序员填写空白:

    A football team is a particular kind of _____



    有没有人说“有一些花里胡哨的足球运动员名单”,或者他们都说“运动队”或“俱乐部”或“组织”?你认为一支足球队是一种特殊类型的球员名单,这是你人类的思想,而且只有你的人类思想。
    List<T>是一种机制。足球队是一个业务对象——也就是说,一个代表程序业务领域中的一些概念的对象。不要混合这些!足球队是一种团队;它有一个名册,名册是一份球员名单。名册不是特定类型的球员名单。名册是球员名单。所以创建一个名为 Roster 的属性那是一个 List<Player> .并使它ReadOnlyList<Player>除非你相信每个了解足球队的人都可以从名单中删除球员。

    Is inheriting from List<T> always unacceptable?



    谁不能接受?我?不。

    When is it acceptable?



    当您构建扩展 List<T> 的机制时机制。

    What must a programmer consider, when deciding whether to inherit from List<T> or not?



    我是在构建机制还是业务对象?

    But that's a lot of code! What do I get for all that work?



    您花了更多时间输入您的问题,否则您需要为List<T> 的相关成员编写转发方法。五十多次。你显然不怕冗长,我们在这里讨论的是非常少量的代码;这是几分钟的工作。

    更新

    我考虑了更多,还有另一个原因是不将足球队建模为球员列表。事实上,将足球队建模为也有球员名单可能是一个坏主意。拥有球员名单的球队的问题在于,你得到的是球队在某一时刻的快照。我不知道你对这个类(class)的商业案例是什么,但如果我有一个代表一支足球队的类(class),我会问它一些问题,比如“2003 年到 2013 年有多少海鹰队球员因伤缺席比赛?”或者“之前为另一支球队效力的丹佛球员中,哪位球员的跑码同比增幅最大?”或“Did the Piggers go all the way this year?

    也就是说,在我看来,一支足球队被很好地建模为历史事实的集合,例如球员何时被招募、受伤、退役等。显然,当前的球员名单是一个重要的事实,应该是最重要的——中心,但您可能还想对这个对象做其他有趣的事情,这些事情需要更多的历史视角。

    关于c# - 为什么不从 List<T> 继承?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51722074/

    相关文章:

    c# - 使用 System.Net.Mail 发送电子邮件时如何捕获邮件大小太大异常?

    c# - 获取Facebook个人资料的正则表达式

    .net - 将事件参数和发送者传递给 RelayCommand

    c# - 在更新到数据库之前编辑 DataTable 中的行

    c# - 使用 LINQ 获取包含自定义对象的列表的总和/平均值

    c# - 从 URL 下载/流式传输文件 - asp.net

    c# - 在没有内存泄漏的情况下清除 ListView 项

    python - 从列表 append 索引无法正常工作

    java - 对列表 - 字符串元素进行排序

    python - 如何在列表理解中添加多个变量