问题描述
在规划我的项目时,我通常会从以下思路开始:
When planning out my programs, I often start with a chain of thought like so:
足球队只是足球运动员的名单.因此,我应该用:
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.
但我后来意识到,除了球员名单之外,球队还有其他属性必须记录下来.例如本赛季总成绩、当前预算、球衣颜色、代表球队名称的字符串
等.
But I realize later that teams also have other properties, besides the mere list of players, that must be recorded. For example, the running total of scores this season, the current budget, the uniform colors, a string
representing the name of the team, etc..
那么我想:
好吧,一个足球队就像一个球员名单,但另外,它有一个名字(一个string
)和一个总得分(一个int
)..NET 没有提供用于存储足球队的类,所以我将创建自己的类.最相似和相关的现有结构是List<FootballPlayer>
,所以我将继承它:
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 (anint
). .NET does not provide a class for storing football teams, so I will make my own class. The most similar and relevant existing structure isList<FootballPlayer>
, so I will inherit from it:
class FootballTeam : List<FootballPlayer>
{
public string TeamName;
public int RunningTotal
}
但事实证明,指南说您不应该从 List<T>
一个>.我在两个方面对这个指南感到非常困惑.
But it turns out that a guideline says you shouldn't inherit from List<T>
. I'm thoroughly confused by this guideline in two respects.
显然 List
以某种方式优化了性能.怎么会这样?如果我扩展 List
会导致什么性能问题?究竟会破坏什么?
Apparently List
is somehow optimized for performance. How so? What performance problems will I cause if I extend List
? What exactly will break?
我看到的另一个原因是 List
是由微软提供的,我无法控制它,所以 在公开公共 API"后,我以后无法更改它.但我很难理解这一点.什么是公共 API,我为什么要关心?如果我当前的项目没有也不太可能有这个公共 API,我可以安全地忽略这个指南吗?如果我确实继承自 List
并且 结果我需要一个公共 API,我会有什么困难?
Another reason I've seen is that List
is provided by Microsoft, and I have no control over it, so I cannot change it later, after exposing a "public API". But I struggle to understand this. What is a public API and why should I care? If my current project does not and is not likely to ever have this public API, can I safely ignore this guideline? If I do inherit from List
and it turns out I need a public API, what difficulties will I have?
为什么它甚至很重要?一个列表就是一个列表.有什么可能改变?我可能想要改变什么?
Why does it even matter? A list is a list. What could possibly change? What could I possibly want to change?
最后,如果微软不希望我从 List
继承,他们为什么不让类 sealed
?
And lastly, if Microsoft did not want me to inherit from List
, why didn't they make the class sealed
?
显然,对于自定义集合,Microsoft 提供了一个应该扩展的 Collection
类,而不是 List
.但是这个类很裸,并没有很多有用的东西,如AddRange
,例如.jvitor83's answer 提供了该特定方法的性能原理,但是慢的 AddRange
不是更好比没有AddRange
?
Apparently, for custom collections, Microsoft has provided a Collection
class which should be extended instead of List
. But this class is very bare, and does not have many useful things, such as AddRange
, for instance. jvitor83's answer provides a performance rationale for that particular method, but how is a slow AddRange
not better than no AddRange
?
从 Collection
继承比从 List
继承要多得多,而且我看不出有什么好处.微软肯定不会无缘无故告诉我做额外的工作,所以我不禁觉得我在某种程度上误解了一些东西,继承 Collection
实际上不是解决我的问题的正确方法.
Inheriting from Collection
is way more work than inheriting from List
, and I see no benefit. Surely Microsoft wouldn't tell me to do extra work for no reason, so I can't help feeling like I am somehow misunderstanding something, and inheriting Collection
is actually not the right solution for my problem.
我看到了一些建议,例如实现 IList
.就是不行.这是几十行样板代码,对我没有任何好处.
I've seen suggestions such as implementing IList
. Just no. This is dozens of lines of boilerplate code which gains me nothing.
最后,有人建议将 List
包装在一些东西中:
Lastly, some suggest wrapping the List
in something:
class FootballTeam
{
public List<FootballPlayer> Players;
}
这样做有两个问题:
这让我的代码变得不必要地冗长.我现在必须调用
my_team.Players.Count
而不仅仅是my_team.Count
.值得庆幸的是,使用 C# 我可以定义索引器以使索引透明化,并转发内部List
的所有方法......但这是很多代码!所有这些工作我能得到什么?
It makes my code needlessly verbose. I must now call
my_team.Players.Count
instead of justmy_team.Count
. Thankfully, with C# I can define indexers to make indexing transparent, and forward all the methods of the internalList
... But that's a lot of code! What do I get for all that work?
这只是简单的没有任何意义.足球队没有拥有"球员名单.它是玩家列表.您不会说John McFootballer 已加入 SomeTeam 的球员".您说John 已加入 SomeTeam".您不会将字母添加到字符串的字符",而是将字母添加到字符串.您不是将一本书添加到图书馆的书籍中,而是将一本书添加到图书馆中.
It just plain doesn't make any sense. A football team doesn't "have" a list of players. It is the list of players. You don't say "John McFootballer has joined SomeTeam's players". You say "John has joined SomeTeam". You don't add a letter to "a string's characters", you add a letter to a string. You don't add a book to a library's books, you add a book to a library.
我意识到幕后"发生的事情可以说是将 X 添加到 Y 的内部列表中",但这似乎是一种非常违反直觉的思考世界的方式.
I realize that what happens "under the hood" can be said to be "adding X to Y's internal list", but this seems like a very counter-intuitive way of thinking about the world.
什么是表示数据结构的正确 C# 方式,逻辑上"(也就是说,对人类思维")只是 things<的
list
/code> 有一些花里胡哨的东西?
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?
从 List<T>
继承总是不可接受的吗?什么时候可以接受?为什么/为什么不?程序员在决定是否继承 List<T>
时必须考虑什么?
Is inheriting from List<T>
always unacceptable? When is it acceptable? Why/why not? What must a programmer consider, when deciding whether to inherit from List<T>
or not?
推荐答案
这里有一些很好的答案.我会向他们补充以下几点.
There are some good answers here. I would add to them the following points.
什么是表示数据结构的正确 C# 方式,逻辑上"(也就是说,对人类思维")只是一个带有一些花里胡哨的事物的列表?
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?
请任何十个熟悉足球存在的非计算机程序员来填空:
Ask any ten non-computer-programmer people who are familiar with the existence of football to fill in the blank:
足球队是一种特殊的_____
A football team is a particular kind of _____
有人说有一些花里胡哨的足球运动员名单",还是他们都说运动队"或俱乐部"或组织"?您认为足球队是特定类型的球员名单的想法存在于您的人类头脑中,并且只存在于您的人类头脑中.
Did anyone say "list of football players with a few bells and whistles", or did they all say "sports team" or "club" or "organization"? Your notion that a football team is a particular kind of list of players is in your human mind and your human mind alone.
List
是一个机制.足球队是一个业务对象——也就是说,一个代表程序业务领域中的某个概念的对象.不要混合这些!足球队是一种球队;它有一个名册,一个名册是一个球员名单.名册不是特定类型的球员名单.名册是球员名单.因此,创建一个名为 Roster
的属性,它是一个 List
.并在您使用它时将其设为 ReadOnlyList<Player>
,除非您相信每个了解足球队的人都会从名单中删除球员.
List<T>
is a mechanism. Football team is a business object -- that is, an object that represents some concept that is in the business domain of the program. Don't mix those! A football team is a kind of team; it has a roster, a roster is a list of players. A roster is not a particular kind of list of players. A roster is a list of players. So make a property called Roster
that is a List<Player>
. And make it ReadOnlyList<Player>
while you're at it, unless you believe that everyone who knows about a football team gets to delete players from the roster.
从 List<T>
继承总是不可接受的吗?
Is inheriting from
List<T>
always unacceptable?
谁不能接受?我?没有.
Unacceptable to who? Me? No.
什么时候可以接受?
当您构建一个扩展List<T>
机制的机制时.
When you're building a mechanism that extends the List<T>
mechanism.
程序员在决定是否继承 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>
的相关成员编写转发方法五十次.您显然不怕冗长,我们在这里讨论的是非常少量的代码;这是几分钟的工作.
You spent more time typing up your question that it would have taken you to write forwarding methods for the relevant members of List<T>
fifty times over. You're clearly not afraid of verbosity, and we are talking about a very small amount of code here; this is a few minutes work.
我想多了,还有另一个理由不将足球队建模为球员名单.事实上,将足球队建模为拥有球员名单可能是个坏主意.球队作为/拥有球员名单的问题在于,你所拥有的是球队在某一时刻的快照.我不知道你对这门课的商业案例是什么,但如果我有一个代表足球队的课,我想问它这样的问题:2003 年至 2013 年间有多少海鹰队球员因伤缺席比赛?"或之前为另一支球队效力的哪位丹佛球员的码数同比增幅最大?"或今年小猪队一路走好吗?"
I gave it some more thought and there is another reason to not model a football team as a list of players. In fact it might be a bad idea to model a football team as having a list of players too. The problem with a team as/having a list of players is that what you've got is a snapshot of the team at a moment in time. I don't know what your business case is for this class, but if I had a class that represented a football team I would want to ask it questions like "how many Seahawks players missed games due to injury between 2003 and 2013?" or "What Denver player who previously played for another team had the largest year-over-year increase in yards ran?" or "Did the Piggers go all the way this year?"
也就是说,在我看来,一支足球队可以很好地建模为历史事实的集合,例如球员何时被招募、受伤、退役等.显然,当前的球员名单是重要的事实可能应该放在前面和中心,但您可能还想对这个对象做其他有趣的事情,需要更多的历史视角.
That is, a football team seems to me to be well modeled as a collection of historical facts such as when a player was recruited, injured, retired, etc. Obviously the current player roster is an important fact that should probably be front-and-center, but there may be other interesting things you want to do with this object that require a more historical perspective.
这篇关于为什么不从 List<T> 继承?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!