我对从公共API方法和属性返回的集合类型感到困惑。
我想到的藏品是IList、ICollection和Collection。
返回这些类型中的一个总是比其他类型更受欢迎,还是取决于具体情况?
- 副本:stackoverflow.com/questions/271170/…
- 另外:stackoverflow.com/questions/4455428/…
ICollection是一个公开集合语义的接口,如Add()、Remove()和Count。
Collection是ICollection接口的具体实现。
IList本质上是一个具有随机顺序访问的ICollection。
在这种情况下,您应该决定您的结果是否需要列表语义,例如基于顺序的索引(然后使用IList),或者您是否只需要返回无序的"包"结果(然后使用ICollection)。
- +一个好答案。但是,我建议添加一些关于抽象的一般原则的注释,以及为什么它倾向于返回接口类型而不是具体的类类型。
- Collection实现IList而不仅仅是ICollection。
- .NET中的所有集合都已排序,因为已排序IEnumerable。IList与ICollection的区别在于,它提供成员使用索引。例如,list[5]工作,但collection[5]不编译。
- 我也不同意订购的托收应该实现IList。有很多有序的集合没有。IList是关于快速索引访问的。
- 值得注意的是,虽然IList与IList是一个通用的等价物,但ICollection和ICollection是非常不同的。实现IList的通用Foo通常不应实现非通用IList,但仍应实现非通用ICollection。否则,即使Foo会实现IEnumerable和ICollection,后者会暴露一个快速的Count属性,IEnumerable.Count()扩展方法也无法使用ICollection.Count()实现。
- @codesinchaos指出collection实现ilist对我来说很奇怪,有人知道为什么这样做吗?
- @.NET集合类和接口的设计和命名都很糟糕。我不太想他们为什么这样称呼他们。
- @很抱歉,参加聚会迟到了,但是collection[i]确实编译了。我错过什么了吗?
- @不,它不是:ICollection collection = new int[1]; Console.WriteLine(collection[0]);。我想你是用Collection试过的,不是ICollection试过的,但这不是我说的。
- @斯威克,是的,你是对的。我以为你的意思是收藏。另一个问题-您所说的IEnumerable是什么意思?这不是一个实现细节吗?顺便说一下,对于内存中的列表,ORDER可能为true,而不是来自数据库的列表。
- @斯维克:所有的托收都是因为IEnumerable是订购的吗?以HashSet为例,它执行IEnumerable但显然没有命令。也就是说,如果重新组织内部哈希表,您对项目的顺序没有影响,并且此顺序可能随时更改。
- @斯维克,你所说的命令是什么意思?我对这个词有点困惑,你是说它像链表一样被链住了吗?
- @永恒,我不是说实现,只是说如果您使用foreach迭代一个集合,您将按集合定义的某种顺序获得元素。
一般来说,您应该返回尽可能通用的类型,即对消费者需要使用的返回数据有足够了解的类型。这样,您就可以更自由地更改API的实现,而不必破坏正在使用它的代码。
还可以考虑将IEnumerable接口作为返回类型。如果只对结果进行迭代,那么消费者不需要更多。
- 但也要考虑ICollection扩展IEnumerable以提供包括Count属性在内的其他实用程序。这很有帮助,因为您可以在不多次遍历序列的情况下确定序列的长度。
- @逆行:实际上,Count方法的实现检查了集合的实际类型,因此它将对某些已知类型(如数组和ICollection)使用Length或Count)属性,即使将其作为IEnumerable提供。
- @哈哈,太棒了,谢谢你的信息!
- @Guffa:值得注意的是,如果一个类Thing实现IList,而不是非泛型ICollection,那么在Thing上调用IEnumerable.Count()会很快,但调用IEnumerable.Count()会很慢(因为扩展方法会寻找ICollection的实现,而不会找到)。但是,如果类实现非泛型ICollection,IEnumerable.Count会找到并使用它。
- 到Enumerable.Count的msdn链接
- 我不同意。最好是返回您拥有的最丰富的类型,这样客户可以直接使用它,如果这是他们想要的。如果您有一个列表<>,为什么只返回一个IEuerable<>来强制调用方不必要地调用ToList()?
- @Guffa如何自动实现C接口????
IList和ICollection的主要区别在于IList允许您通过索引访问元素。IList描述类似数组的类型。ICollection中的元素只能通过枚举访问。两者都允许插入和删除元素。
如果您只需要枚举集合,那么最好使用IEnumerable。它有两个优势:
它不允许对集合进行更改(但不允许对引用对象进行更改,如果这些对象属于引用类型)。
它允许尽可能多的源,包括从算法上生成的、完全不是集合的枚举。
Collection是一个基本类,主要对集合的实现者有用。如果您在接口(API)中公开它,那么将排除许多不是从它派生的有用集合。
IList的一个缺点是数组实现它,但不允许添加或删除项(即不能更改数组长度)。如果在数组上调用IList.Add(item),则会引发异常。由于IList有一个布尔属性IsReadOnly,您可以在尝试执行此操作之前检查该属性,因此情况有所缓解。但在我看来,这仍然是图书馆的设计缺陷。因此,当需要添加或删除项目时,我直接使用List。
- 代码分析(和fxcop)建议在返回具体的泛型集合时,应返回以下内容之一:Collection、ReadOnlyCollection或KeyedCollection。而且不应退还List。
- @davidrr:通常将列表传递给必须添加或删除项的方法是有用的。如果将参数键入IList,则无法确保不会使用数组作为参数调用该方法。
IList是所有通用列表的基本接口。因为它是一个有序的集合,所以实现可以决定顺序,从排序顺序到插入顺序不等。此外,Ilist具有item属性,允许方法根据索引读取和编辑列表中的条目。这样就可以在位置索引处插入、删除列表中的值。
另外,由于IList : ICollection,ICollection中的所有方法也可以在这里执行。
ICollection是所有通用集合的基本接口。它定义大小、枚举器和同步方法。可以在集合中添加或删除项,但由于缺少索引属性,无法选择项在哪个位置发生。
Collection为IList、Ilist和IReadOnlyList提供了一个实现。
如果使用更窄的接口类型,如ICollection,而不是IList,则可以保护代码不被破坏。如果使用更广泛的接口类型,如IList,则更容易破坏代码更改。
从一个来源引用,
ICollection, ICollection : You want to modify the collection or
you care about its size.
IList, IList: You want to modify the collection and you care about the ordering and / or positioning of the elements in the collection.
返回接口类型更为一般,因此(缺少关于您的特定用例的进一步信息)我倾向于这一点。如果要公开索引支持,请选择IList,否则ICollection就足够了。最后,如果要指示返回的类型是只读的,请选择IEnumerable。
如果你以前没有读过,BradAbrams和KrzysztofCwalina写了一本很棒的书,题为"框架设计指南:可重用.NET库的惯例、习惯用法和模式"(你可以从这里下载摘要)。
- + 1。良好的阅读材料。
- IEnumerable是只读的?当然,但在存储库的上下文中重新调整它时,即使在执行tolist之后也是不安全的。问题是,当原始数据源获取gc时,ienumarble.tolist()不能在多线程上下文中工作。它在线性模式下工作,因为GC在您完成工作后会被调用,但是使用async,现在4.5+ienumarbale中的一天会变成一种球痛。
这个问题有一些主题:
- 接口与类
- 从几个相似的类、集合、列表、数组中选择哪个特定类?
- 公共类与子项("泛型")集合
您可能想强调它是一个面向对象的a.p.i。
接口与类
如果您对接口没有太多经验,我建议您坚持使用类。我看到很多时候开发人员都会跳到界面上,即使这不是必须的。
最后做一个糟糕的界面设计,而不是一个好的类设计,顺便说一下,它最终可以移植到一个好的接口设计中…
在A.P.I.中你会看到很多接口,但是,不要急着去做,如果你不需要的话。
您最终将学习如何将接口应用到代码中。
从几个相似的类、集合、列表、数组中选择哪个特定类?
C(dotnet)中有几个类可以互换。如前所述,如果您需要一个更具体的类,例如"canBesortedClass",那么在您的a.p.i中明确说明它。
您的a.p.i.用户真的需要知道,您的类可以被排序,或者对元素应用某种格式吗?然后使用"canbesortedclass"或"elementscanbeipaintedclass",否则使用"GenericBrandClass"。
否则,使用更一般的类。
公共集合类与子项("泛型")集合
你会发现有些类包含其他元素,您可以指定所有元素都应为特定类型。
泛型集合是可以使用同一集合的类,对于多个代码应用程序,无需创建新集合,对于每个新的子项类型,如:集合。
您的a.p.i.用户是否需要一个非常特定的类型,所有元素都是相同的?
使用类似于List的东西。
您的a.p.i.用户是否需要几个相关类型?
揭发List给你的a.p.i.,在内部使用ListList、List,其中Orange、Banana和Strawberry是Fruit的后代。
您的a.p.i.用户是否需要泛型类型集合?
使用List,其中所有项目都是object。
干杯。
- "如果您对接口没有太多经验,我建议您坚持使用类"这不是一个好建议。这就像是说,如果有什么你不知道的,就离它远点。接口非常强大,支持"程序到抽象与具体"的概念。由于能够通过模拟交换类型,它们对于进行单元测试也非常强大。除了这些评论之外,还有很多其他的重要原因需要使用界面,所以请尽一切努力去探索它们。
- @atconway我经常使用接口,但是,作为许多概念,它是一个强大的工具,可以很容易地混合使用。有时,开发人员可能不需要它。我的建议不是"永远不要接口",我的建议是"在这种情况下,你可以跳过接口。"了解它,以后你可能会得到最好的。干杯。
- 我建议总是对接口进行编程。它有助于保持代码松散耦合。
- @MAC10688我以前的回答(atconway)也适用于您的评论。