返回“ IList” vs“ ICollection” vs“ Collection”


Answers:


71

通常,您应该返回一种尽可能通用的类型,即,该类型仅知道使用者需要使用的返回数据。这样,您就拥有更大的自由来更改API的实现,而无需破坏正在使用它的代码。

还可以将IEnumerable<T>接口视为返回类型。如果结果只是要迭代,那么消费者就不需要更多了。


25
但是还要考虑ICollection <T>,它扩展了IEnumerable <T>以提供包括Count属性的其他实用程序。这将很有帮助,因为您可以确定序列的长度,而无需多次遍历序列。
retrodrone 2013年

11
@retrodrone:实际上,该Count方法的实现会检查集合的实际类型,因此它将对某些已知类型(例如数组)使用LengthCount属性,ICollection甚至当您将其作为提供时也是如此IEnumerable
Guffa 2013年

8
@Guffa:值得一提的是,如果一个类Thing<T>实现IList<T>而不是非泛型ICollection,则调用IEnumerable<Cat>.Count()a Thing<Cat>会很快,但是调用IEnumerable<Animal>.Count()会很慢(因为扩展方法将查找而不是找到的实现)ICollection<Cat>)。如果该类实现了非泛型ICollectionIEnumerable<Animal>.Count则会找到并使用它。
2013年


8
我不同意。最好返回您拥有的最丰富的类型,以便客户在需要时可以直接使用它。如果您有List <>,为什么只返回IEnuerable <>来强制调用者不必要地调用ToList()?
zumalifeguard

140

ICollection<T>是暴露集合语义诸如接口Add()Remove()Count

Collection<T>是该ICollection<T>接口的具体实现。

IList<T>本质上是ICollection<T>基于随机订单的访问。

在这种情况下,您应该决定结果是否需要列表语义,例如基于订单的索引(然后使用IList<T>),或者是否只需要返回无序的结果“袋”(然后使用ICollection<T>)。


5
Collection<T>实现IList<T>不仅ICollection<T>
CodesInChaos 2012年

56
.Net中的所有集合都是有序的,因为它IEnumerable<T>是有序的。有什么区别IList<T>ICollection<T>是,它为会员提供与索引工作。例如list[5]可行,但collection[5]不会编译。
svick 2012年

5
我也不同意应执行有序集合IList<T>。有很多没有的有序集合。IList<T>关于快速索引访问。
CodesInChaos 2012年

4
@ .yo .net集合类和接口的设计和命名很简单。我不会想太多为什么他们这么称呼他们。
CodesInChaos

6
@svick:是否IEnumerable<T>已订购所有收藏品,因为已订购?拿HashSet<T>-它实现了,IEnumerable<T>但显然没有命令。也就是说,您对项目的顺序没有任何影响,并且如果内部哈希表已重新组织,则此顺序可以随时更改。
Olivier Jacot-Descombes

61

IList<T>和之间的主要区别ICollection<T>是,IList<T>您可以通过索引访问元素。IList<T>描述类似数组的类型。中的元素ICollection<T>只能通过枚举访问。两者都允许插入和删除元素。

如果只需要枚举集合,则IEnumerable<T>是首选。与其他优点相比,它有两个优点:

  1. 它不允许更改集合(如果元素是引用类型,则不允许更改)。

  2. 它允许尽可能多的来源,包括通过算法生成的枚举,而这些枚举根本不是集合。

Collection<T>是基类,主要对集合的实现者有用。如果您在接口(API)中公开它,则许多不是从它派生的有用集合将被排除。


缺点之一IList<T>是数组实现了它,但不允许您添加或删除项(即您不能更改数组长度)。如果调用IList<T>.Add(item)数组,将引发异常。这种情况有些令人困惑,因为您可以在尝试执行此操作之前先检查IList<T>其布尔属性IsReadOnly但是在我看来,这仍然是库中的设计缺陷。因此,List<T>当需要添加或删除项目时,我直接使用。


代码分析(和的FxCop)建议在返回一个具体的泛型集合时,下列情况之一应返回:CollectionReadOnlyCollectionKeyedCollection。那List不应该被退回。
DavidRR 2015年

@DavidRR:通常,将列表传递给必须添加或删除项的方法很有用。如果将参数键入为IList<T>,则无法确保不会将数组作为参数调用该方法。
奥利维尔·雅各布·德斯科姆斯

7

IList<T>是所有通用列表的基本接口。由于它是有序集合,因此实现可以决定排序,范围从排序的顺序到插入顺序。而且Ilist具有Item属性,该属性允许方法根据索引来读取和编辑列表中的条目。这样就可以在位置索引处从列表中插入值或从列表中删除值。

此外,由于IList<T> : ICollection<T>ICollection<T>此处的所有方法也都可用于实现。

ICollection<T>是所有通用集合的基本接口。它定义了大小,枚举器和同步方法。您可以将项目添加或删除到集合中,但是由于缺少index属性,您无法选择它发生在哪个位置。

Collection<T>提供了一个实现IList<T>IListIReadOnlyList<T>

如果您使用较窄的接口类型,例如 ICollection<T>而不是的IList<T>,则可以保护您的代码以防破坏更改。如果使用更广泛的接口类型(例如)IList<T>,则更有可能破坏代码更改。

来源报价

ICollectionICollection<T>:你想修改的集合或者你关心它的大小。 IListIList<T>:你想修改的收集和你关心的集合中的元素的顺序和/或定位。


5

返回接口类型更为通用,因此(在您的特定用例上缺少更多信息)我倾向于这样做。如果要提供索引支持,请选择IList<T>,否则ICollection<T>就足够了。最后,如果要指示返回的类型为只读,请选择IEnumerable<T>

而且,如果您以前从未读过它,Brad Abrams和Krzysztof Cwalina写了一本很棒的书,标题为“框架设计准则:可重用.NET库的约定,惯用语和模式”(您可以从此处下载摘要)。


IEnumerable<T>是只读的?可以,但是即使在执行ToList之后在存储库的上下文中对其进行重新调整也不是线程安全的。问题是原始数据源获取GC时,IEnumarble.ToList()在多线程上下文中不起作用。它工作于线性状态,因为在您完成工作后会调用GC,但async现在在4.5+的版本中使用IEnumarbale变得越来越痛苦。
Piotr Kula

-3

这个问题有一些主题:

  • 接口与类
  • 从几个相似的类,集合,列表,数组中,哪个特定的类?
  • 通用类与子项(“泛型”)集合

您可能要强调它是一个面向对象的API

接口与类

如果您对接口没有太多经验,我建议您坚持使用类。我看到很多时候开发人员都跳到了接口,即使它不是必须的。

而且,最终要做一个糟糕的界面设计,而不是一个好的类设计,顺便说一句,最终可以将其移植到一个好的界面设计中。

您会在API中看到很多接口,但是,如果您不需要它,请不要着急。

您最终将学习如何将接口应用于代码。

从几个相似的类,集合,列表,数组中,哪个特定的类?

c#(dotnet)中有几个可以互换的类。如前所述,如果您需要更具体的类中的内容(例如“ CanBeSortedClass”),请在您的API中明确显示。

您的API用户是否真的需要知道,您的类可以排序,还是将某些格式应用于元素?然后使用“ CanBeSortedClass”或“ ElementsCanBePaintedClass”,否则使用“ GenericBrandClass”。

否则,请使用更通用的类。

通用集合类与子项(“泛型”)集合

您会发现有些类包含其他元素,并且可以指定所有元素应为特定类型。

泛型集合是可以为多个代码应用程序使用相同集合的类,而不必为每个新的子项目类型创建新集合,例如:Collection

您的API用户是否需要一个非常特定的类型,所有元素都一样?

使用类似的东西List<WashingtonApple>

您的API用户是否需要几种相关类型?

暴露List<Fruit>你的API,并使用List<Orange> List<Banana>List<Strawberry>在内部,在那里OrangeBananaStrawberry从后代Fruit

您的API用户是否需要泛型类型集合?

使用List,其中所有项目均为object

干杯。


7
“如果您对接口没有太多经验,我建议您坚持使用类”。这不是一个好建议。这就像在说如果您不知道某些东西,那就远离它。接口非常强大,并且支持“从程序设计到​​抽象或具体化”的概念。由于可以通过模拟互换类型,因此它们对于进行单元测试也非常强大。除了这些注释之外,还有很多其他使用接口的重要原因,因此请务必进行探索。
atconway

@atconway我经常使用接口,但是,从许多概念来看,它是一个功能强大的工具,可以很容易地混合使用。而且,有时,开发人员可能不需要它。我的建议不是“永远不要接口”,我的建议是“在这种情况下,您可以跳过这些接口。对其进行了解,以后可以充分利用它们”。干杯。
umlcat

1
我建议始终对接口进行编程。它有助于使代码保持松散耦合。
mac10688 2014年

@ mac10688我先前的回答(在conconway上)也适用于您的评论。
umlcat 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.