【问题标题】:Why use ICollection<T> over IList<T>?为什么使用 ICollection<T> 而不是 IList<T>?
【发布时间】:2020-02-10 23:19:51
【问题描述】:

我读过文章说如果你想要IEnumerable&lt;T&gt; 的功能,但也想要Count 属性,请使用ICollection&lt;T&gt;

但是,既然System.Linq.Enumerable 中的扩展方法提供了诸如ElementAt() 之类的方法,为什么不直接使用IList&lt;T&gt;,因为它使用了索引器?

在我看来,ICollection&lt;T&gt; 可以做 IList&lt;T&gt; 可以做的事情,只是以一种更冗长且可读性更低的方式。

在哪种情况下使用ICollection&lt;T&gt;IList&lt;T&gt; 更易读/更有效,更符合 SOLID 原则,或者在某些情况下使用ICollection&lt;T&gt; 更好?

编辑:

重复问题的答案没有回答我的问题,因为他们避免谈论 ICollection&lt;T&gt; 可以做 IList&lt;T&gt; 所做的事情,但方式更丑陋。

如果他们都做同样的事情,为什么不使用清洁剂IList&lt;T&gt;

我在这里得到了更好的答案: https://www.reddit.com/r/csharp/comments/dl9xao/why_use_icollectiont_over_ilistt/

【问题讨论】:

  • 所以你的意思是我可以调用你的方法或读取你的属性,取回一个列表,然后开始向其中添加元素?
  • 您的问题中涉及的 3 种类型告诉我不同​​的事情。 IEnumerable 告诉我,我将获得一组项目。 ICollection 告诉我,我将获得一组项目,以保证轻松快速地访问其中的项目数量。 List 告诉我我会得到一个我可以操作的集合。您必须决定在每种情况下哪个级别是合适的。
  • 但是 ICollection 为您提供了一个您也可以操作的集合。 ICollection 似乎与 IList 具有所有相同的功能,只是没有那么干净。
  • 很遗憾,我觉得这个问题跑题了,因为它间接征求人们的意见和建议。没有固定的单一答案说“在那个时候返回这个,否则这个其他的东西”。关于您是否应该退回其中一个,我有几种意见,这取决于很多因素,但归结为我的意见。
  • 我认为您的问题实际上是重复的,并且由这些答案(广泛)解决 - 这是一个用例场景。一种这样的场景是使用IDictionarydynamic 类型(System.Dynamic.ExpandoObject)。

标签: c# list ienumerable icollection


【解决方案1】:

如果您从开销的角度来看这个问题,我认为您最好选择两者之间的中介 ISet&lt;T&gt;HashSet&lt;T&gt; 的实现。

Set 经常被忽略,但在确定“所有权”方面非常有效,并且由于不依赖键而节省空间,因为它们不关心排序。

编辑(2020 年 11 月 18 日) - 请务必注意,将项目添加到 HashSet 后,将计算其 HashCode。这样做的成本可能很高,因此完全抵消了您可能从更快的查找中获得的任何性能优势。

重要的区别在于 Add()Remove()Contains() 都变为 o(1)(相对于 IList&lt;T&gt; 的典型 o(n) 成本。

如果订购成为问题,还有SortedSet&lt;T&gt;

这是一个很棒的article 解释这比我能做的要深入得多。一个重要的sn-p:

HashSet&lt;T&gt; 是如果您想要尽可能快的查找但不关心顺序的选择集。相比之下,SortedSet&lt;T&gt; 将为您提供排序后的集合,但性能略有下降。

【讨论】:

  • 这里有一些误导性信息——散列集虽然非常适合快速查找,但并不总是更好。您需要记住,将某些内容添加到哈希集中需要计算项目的哈希,根据特定的用例,这可能会完全抵消任何查找性能优势。理解它并明智地使用它......
  • 如果您仔细阅读,我并没有像您暗示的那样声称 HashSet 是灵丹妙药。我指定了它们可以在特定用例中带来的价值,甚至概述了最佳用例。
  • 仔细阅读后,我仍然没有看到 HashSet 的任何隐含缺点。但是,这应该很容易补救。一旦您更新它以提供更平衡的观点,我很乐意投票。 HashSet 确实经常被忽视,非常欢迎您在这一点上做出贡献!
  • @IgorPashchuk 已更新以包含您关于哈希码的注释
【解决方案2】:

简短的回答是,您可以将 IList 用于任何元素列表。实际上,IList 实现了 ICollection,而 ICollection 实现了 IEnumerable。您可以根据上下文决定您需要的抽象级别。

详情请见this link

【讨论】:

  • 好吧...我不会对您投反对票(鉴于您的代表),但这里的问题陈述反映了标题:“它会是什么场景......更好......使用ICollection&lt;T&gt; 超过 IList&lt;T&gt;。所以,这个答案与问题无关,也与调查无关。
  • 另外,您可能应该使用诸如“实现”甚至“派生”之类的术语而不是子接口来与我们更习惯的 OOP 材料相关联。此外,您是否应该以分层方式考虑接口是值得怀疑的。我实际上还没有深入研究它来给出确定性,但这归结为您是否能够在运行时确定是否存在某种类型实例的基接口的派生接口;如果可以做到这一点,我可以看到以这些方式思考的优点。
  • 感谢@brett-caswell 的评论。相应地更新了答案。
【解决方案3】:

答案非常主观。更重要的是你需要多少。

  • 如果您只需要遍历所有项目,则首选IEnumerable&lt;T&gt;
  • 如果您需要处理集合(AddRemove 等),应使用ICollection&lt;T&gt;
  • 如果您要经常使用索引,请使用IList&lt;T&gt;
  • 如果您打算进行各种操作,请继续使用List&lt;T&gt;

但是在这些情况下,有 IReadonlyCollcetion&lt;T&gt;IReadonlyList&lt;T&gt;,这使得返回的集合不可变,并且应该再次出现在 IEnumerable 之后,但优先于 ICollection&lt;T&gt;IList&lt;T&gt;

最终目标始终是尽可能使用抽象类型。这是基于Code for the interfaces not for the implementation 的原则。

编辑:对于这个问题,我想说索引的需要是首选一个而不是另一个的主要因素。

【讨论】:

  • 有争议的意见。当我的意思是多次迭代可枚举时,我要求IReadOnlyCollection。我还制作了不能多次迭代的枚举。
  • 多个枚举以及无序枚举IReadOnlyColection确实会更好。类似的答案是,如果 foreach 总是 IEnumerable 有效,如果是 for 循环,那么 IReadOnlyCollection。但同样,即使 for 循环可以完成 foreach 所做的所有事情,我们还是尽可能使用 foreach。
猜你喜欢
  • 2021-04-16
  • 1970-01-01
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 2011-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多