为什么IList<T>没有提供List<T>所有的方法?我应该使用哪个?

7
我一直被教导编程应该针对接口进行,所以我的方法参数我会设置为 IList<T> 而不是 List<T>
但这意味着我必须将其转换为 List<T> 才能使用某些方法,比如其中一个方法是 Find
为什么会这样?我应该继续针对接口进行编程,但继续强制转换或还原吗?
我有点困惑为什么 Find(例如)在从中继承的 List<T> 上不可用于 IList<T>

3
我怀疑你上句话中使用的术语强调了你的困惑。类不能“继承”一个接口,而是“实现”它。如果接口不要求实现特定功能(比如Find),那么就没有必要实现它。这和继承关系不同,派生类会继承基类定义的任何函数。 - Cody Gray
可能是[C#- List <T>或IList <T>]的重复内容(https://dev59.com/h3RC5IYBdhLWcg3wJNcN)。 - nawfal
8个回答

12

个人而言,我会使用 IList<T> 而不是 List<T>,然后使用 LINQ (Select, Where等)代替 List 特定的方法。

将类型转换为 List<T> 会削弱使用 IList<T> 的优点,在执行时实现可能是除了 List<T> 以外的其他类型,这实际上会使其更加危险。


一个非常好的观点,我之前没有看到过。让你既能拥有你的界面,又能享用它。 - dove
谢谢Jon!非常好的解释和完美的解决方案。谢谢! - Martin

6

对于列表,您可以继续使用接口进行编程,并使用LINQ来过滤对象。您甚至可以使用更高层次的IEnumerable<T>

但是,如果API的消费者需要调用特定方法,则可能没有选择正确的接口来公开。


感谢Darin的评论。 - Martin

3
我有一点困惑,为什么 Ilist 没有 Find 方法呢?而 List 继承于 IList。
虽然我不知道设计人员的决策过程,但他们可能在考虑以下几个问题:
1)将这些方法放在 IList 上会使契约意图更加明确。根据 MSDN,IList "表示可以通过索引单独访问的对象集合。" 添加 Find 将会改变契约,使其成为可搜索和可索引的集合。
2)在接口上添加每个方法都会增加实现接口的难度。如果所有这些方法都在 IList 上,那么实现 IList 将会更加繁琐。特别是因为:
3) 大多数这些方法的实现是相同的。在 List 上的 Find 和其他一些方法实际上更适合放在辅助类中。例如,ReadOnlyCollection、Collection、ObservableCollection 和 ReadOnlyObservableCollection。如果我必须在所有这些集合上实现 Find(在 LINQ 之前),我会创建一个辅助类,接受 IEnumerable 和谓词,然后循环遍历集合并使实现调用辅助方法。
4)LINQ(不是没有使用原因,而是未来不需要)。借助 LINQ 和扩展方法,现在所有的 IEnumerable 都“具有”Find 作为扩展方法 (只不过它们称之为 Where)。

1

我认为这是因为IList可以是不同的集合类型(例如某种IEnumerable,数组等)。

您可以使用System.Linq中的Where扩展方法。避免将IList强制转换回List。


1

如果你发现在各个类之间传递的IList<T>参数一直被重新转换为List<T>,那么这表明你的设计存在根本性问题。

从你所描述的情况来看,很明显你想要使用多态性,但是一直将其重新转换为List<T>则意味着IList<T>没有你需要的多态性水平。

另一方面,你可能只是针对错误的多态方法进行操作(例如使用Find而不是FirstOrDefault)。

无论哪种情况,你都应该审查你的设计并确定你想要实现什么,然后根据实际需求选择List<T>IList<T>,而不是追求风格的一致性


1
如果您使用 IList<> 参数公开方法,则有人可以传递一个 ReadOnlyCollection<>,它是 IList<> 但不是 List<>。因此,您的 API 将在运行时崩溃。 如果您使用 IList<> 参数公开公共方法,则不能假设它是 IList<> 的特定实现。您必须将其视为 IList<>,而不能再多。

同意。我也会这么说,但是在返回值时,相反的情况是正确的。你应该始终返回你拥有的对象的最丰富版本。所以,如果你的方法构建了一个List,请不要将其降级为IList或IEnumerable。这样做没有任何优势。调用者可以将其用作List、IList或IEnumerable。此外,如果你将其降级,而调用者需要更丰富的版本,他们将被迫调用ToList(),这是可以避免的。唯一的例外是,如果设计接口倾向于使用更通用的类型。 - zumalifeguard

0

如果列表是 API 或服务的一部分,则最好将其作为 IList,以允许在内部更改实现。

关于这个话题已经有很多讨论了。


0

不,这种情况下编程到接口是没有意义的,因为你的List不是IList,上面有额外的方法。


如果我创建一个实现了两个具有不同功能的接口的类,那么这个类既不是这两个接口中的任何一个?(但将其强制转换为任一接口是完全合法的。)这是什么样的推理? - user
1
当您调用它上的find方法时,它不是一个IList,对吗?这个想法是,如果您想要变化实现,您应该编程接口。在这种情况下,实现被强制执行(因此需要转换回List),在一个美好的世界里,我们会有IList的子接口来实现find()方法,但是由于我们没有这样的接口,因此IList接口就无法适用。 - Victor Ionescu
如果您将其视为IList,则首先永远不会调用Find,因为该方法未由IList接口定义,因此它不会编译。(作为比我第一条评论更好的示例,您可以拥有一个继承自另外两个接口的接口,将它们组合在一起,并实现复合接口的类;您可以在任何期望其中任何接口的地方使用该类类型,如果您确切知道自己在做什么,可以将其视为更专业的接口或实现。) - user
然而,这并不意味着List "不是" IList。任何实现IList的类都是IList - 可能还实现了其他许多东西,具体取决于它实现了什么。 - user
是的,你说得对,将一个适当的接口加入到混合中,但不是IList。 - Victor Ionescu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接