服务接口中使用 IList、ICollection 还是 IEnumerable?

6
我正在构建一个与Entity Framework在ASP.NET MVC 5中访问我的数据库的服务。因此,我首先编写了一个带有接口的基础服务。但是我担心一个小事情。
通常我在我的接口中返回一个IEnumerable<T>,但这经常导致代码中的ToList()调用。
所以我想知道在这样的接口中返回什么最好。我可以只返回IList,但我担心它可能太多,而且我不需要IList提供的所有方法。
以下是我的代码:
public interface IBaseService<T>
{
    T Add(T obj);
    T Update(T obj);
    T Remove(string id);
    T Get(string id);

    ICollection<T> Find(Expression<Func<bool, T>> func);
    ICollection<T> GetAll();
}

5
这取决于你希望如何使用这个集合!如果只想让人们能够迭代它,那么请使用 IEnumerable;如果想让人们能够添加或删除集合中的项目,则应使用 IListICollection - Callum Linington
1
IReadOnlyList<T> 可能是什么? - Marc Gravell
我唯一不会使用的是IEnumerable,因为它可能会导致未计算的查询残留问题,或者调用者几乎总是调用ToList()ToArray()来处理结果。 - Martin Costello
3个回答

6

你说过:

通常我在接口中返回一个IEnumerable,但这经常导致代码中出现ToList()调用。

你需要找出为什么要在结果上调用ToList()。可能性很小,但有可能你想修改结果(添加/删除项)。从类似于FindGetAll的方法中返回可修改的集合通常不是一个好习惯,但你可以返回ICollection<T>IList<T>(如果你还需要通过索引器快速访问或插入/删除特定位置)。

然而,更有可能的是你调用ToList来确定有多少元素和/或访问特定元素。如果只需要Count,则返回IReadOnlyCollection<T>,它扩展了IEnumerable<T>,只包含Count属性。如果还需要通过索引器快速访问,则返回IReadOnlyList<T>,它扩展了IReadOnlyCollection<T>,并包含索引器。

ArrayList<T>都实现了所有这些接口,因此你可以直接返回这些类:

 static IReadOnlyList<string> Test() {
      return new string[0]; // fine
 }

然而,以这种方式返回 List 时 - 调用者可能会将其强制转换回 IList<T> 并进行修改。通常情况下这不是问题,但如果你想要防止这种情况发生,请在列表上使用 AsReadOnly(它不会将任何内容复制到新列表中,所以不必担心性能):

static IReadOnlyList<string> Test() {
      return new List<string>().AsReadOnly(); // not required but good practice
 }

这正是我的情况,我经常需要使用Linq/Lambda查询来获取结果中的特定元素或以特殊方式对其进行排序。 因此,我认为大多数情况下我会选择IReadOnlyCollection<T>(正如这里的每个人所指出的)。 谢谢! - Antoine Thiry
请注意,IReadOnlyCollection 无法帮助您“获取结果中的特定元素”,因为这需要索引器,而它没有。您可以使用 LINQ 的 ElementAt,但它实际上会循环整个集合,直到它到达目标索引,除非您返回的真实类型也实现了 IList。因此,如果快速访问(通过索引器)很重要,则需要返回 IList<T> - Evk
好的,如果我理解正确的话,如果我需要Count属性(例如在foreach中使用结果),我返回IReadOnlyCollection<T>,但是如果我需要通过索引器快速访问,我返回IList<T> - Antoine Thiry
抱歉,我之前在你的情况下有点错误地使用了 IList<T>,因为还有另一个接口 - IReadOnlyList<T> 它完美地适合你的场景,因为它既有计数器又有索引器,但是是只读的。我已经更新了我的答案。 - Evk
更好了!非常感谢,现在非常清晰。 - Antoine Thiry

2

这取决于你的意图。在大多数情况下,你的返回类型应该是IReadOnlyCollection<T>。在极少数情况下,如果你希望调用者能够修改集合,则可以返回ICollection<T>。仅在确定调用者仅需要正向迭代时才使用IEnumerable<T>


1
我猜最纯粹的解决方案是使用 IEnumerable<T>,但这取决于在你的解决方案中设定一个约定。如果你负责架构,你可能会决定返回 List<T>,以便拥有更广泛的方法集的舒适感。

始终从公共方法返回接口或抽象类型,而不是具体实现。 - Fred

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