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

我对应该从公共 API 方法和属性返回哪种集合类型感到困惑。

我想到的集合是 IListICollectionCollection

返回这些类型中的一种总是优先于其他类型,还是取决于具体情况?

72758 次浏览

ICollection<T>是一个公开集合语义(如 Add()Remove()Count)的接口。

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

IList<T>本质上是一个具有随机基于顺序访问的 ICollection<T>

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

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

还要考虑返回类型为 IEnumerable<T>接口。如果只是迭代结果,那么消费者不需要更多。

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

如果您只需要枚举一个集合,那么首选 IEnumerable<T>。与其它方式相比,它有两个优势:

  1. 它不允许对集合进行更改(但不允许对元素进行更改,如果元素是引用类型的话)。

  2. 它允许尽可能多的源,包括根据算法生成的、根本不是集合的枚举。

  3. 允许 懒惰的评估,并且可以使用 LINQ 进行查询。

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


IList<T>的一个缺点是,数组实现了它,但不允许您添加或删除项(即不能更改数组长度)。如果对数组调用 IList<T>.Add(item),将引发异常。由于 IList<T>有一个布尔属性 IsReadOnly,您可以在尝试这样做之前检查它,因此这种情况有所缓解。但在我看来,这仍然是图书馆的一个设计缺陷.因此,当需要添加或删除项目时,我直接使用 List<T>


我应该选择哪一个? 让我们仅考虑 List<T>IEnumerable<T>作为专门化/广义类型的例子:

  • 方法 输入参数
    • IEnumerable<T>对调用者具有最大的灵活性。对实现者具有限制性,只读。
    • List<T>对调用者的限制。给予实现者灵活性,可以操作集合。
  • 方法 输出参数或 < strong > 返回值
    • IEnumerable<T>限制呼叫者,只读。实现者最大的灵活性。允许返回关于任何集合或实现迭代器(yield return)。
    • List<T>调用者最大的灵活性,可以操作返回的集合。对实现者有限制。

现在你可能会失望因为我没有给你一个简单的答案。像 “总是用这个作为输入,用那个作为输出”这样的声明是没有建设性的。实际情况是它取决于用例。像 void AddMissingEntries(TColl collection)这样的方法必须提供具有 Add方法的集合类型,或者为了提高效率甚至可能需要 HashSet<T>。方法 void PrintItems(TColl collection)可以愉快地与 IEnumerable<T>一起生活。

返回接口类型更为普遍,因此(缺乏关于特定用例的进一步信息)我倾向于这样做。如果要公开索引支持,请选择 IList<T>,否则选择 ICollection<T>就足够了。最后,如果要指示返回的类型是只读的,请选择 IEnumerable<T>

如果你以前没有读过,Brad Abrams 和 Krzysztof Cwalina 写了一本很棒的书,名为《框架设计指南: 约定、习语和可重用模式》。NET 库”(您可以从 给你下载摘要)。

这个问题引出了一些主题:

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

您可能需要突出显示它是 面向对象应用程序接口。

接口与类的对比

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

并且,最终做了一个糟糕的接口设计,而不是一个好的类设计, 顺便说一下,最终可以迁移到一个良好的界面设计..。

你会在 API 中看到很多接口,但是,不要着急, 如果你不需要的话。

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

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

C # (dotnet)中有几个类可以互换。如前所述,如果您需要从更具体的类(如“ CanBeSortedClass”)中获取某些内容,那么在您的 API 中将其显式化。

你的 API 用户真的需要知道,你的类可以被排序,或者对元素应用某种格式吗?然后使用“ CanBeSortedClass”或“ ElementsCanBePaintedClass”, 否则使用“ GenericBrandClass”。

否则,使用更通用的类。

公共集合类与子项(“泛型”)集合

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

泛型集合 是那些可以使用相同集合的类, 对于多个代码应用程序,无需创建新集合, 对于每个新的子项类型,如下所示: 收集

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

使用类似 List<WashingtonApple>的东西。

你的 API 用户需要几种相关的类型吗?

暴露 List<Fruit>为您的 API,并在内部使用 List<Orange> List<Banana>List<Strawberry>,其中 OrangeBananaStrawberry是从 Fruit的后代。

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

使用 List,其中所有项目都是 object(s)。

干杯。

IList<T>是所有泛型列表的基本接口。因为它是一个有序的集合,所以实现可以决定排序,从排序顺序到插入顺序。此外,Ilist具有 Item 属性,该属性允许方法根据索引读取和编辑列表中的条目。 这样就可以在位置索引处插入或从列表中移除一个值。

此外,自 IList<T> : ICollection<T>以来,所有来自 ICollection<T>的方法也可以在这里实现。

ICollection<T>是所有泛型集合的基本接口。它定义了大小、枚举数和同步方法。可以向集合中添加或移除项,但由于没有索引属性,无法选择在哪个位置添加项。

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

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

引用 来源的话,

ICollectionICollection<T>: 要修改集合或 你在乎它的大小。 IListIList<T>: 您希望修改集合,并关心集合中元素的排序和/或定位。