您更喜欢哪种接口:T ,IEnumerable ,IList 或其他?

好的,我希望整个社区能够帮助我们解决一段时间以来一直在进行的工作场所辩论。 这与定义接受或返回某种类型列表的接口有关。 有几种方法可以做到这一点:

public interface Foo { Bar[] Bars { get; } IEnumerable Bars { get; } ICollection Bars { get; } IList Bars { get; } } 

我自己的偏好是使用IEnumerable作为返回值的参数和数组:

 public interface Foo { void Do(IEnumerable bars); Bar[] Bars { get; } } 

我对这种方法的论点是,实现类可以直接从IEnumerable创建一个List,并简单地用List.ToArray()返回它。 但是有些人认为应该返回IList而不是数组。 我在这里遇到的问题是,现在你需要再次使用ReadOnlyCollection复制它,然后再返回。 返回IEnumerable的选项对于客户端代码来说似乎很麻烦?

你用什么/喜欢什么? (特别是关于将由组织外部的其他开发人员使用的库)

我的偏好是IEnumerable 。 任何其他建议的接口都给出了允许消费者修改底层集合的外观。 这几乎肯定不是你想要做的,因为它允许消费者默默地修改内部集合。

另一个好的恕我直言,是ReadOnlyCollection 。 它允许所有有趣的.Count和Indexer属性,并明确地向消费者说“你无法修改我的数据”。

我没有返回数组 – 它们在创建API时确实是一种非常糟糕的返回类型 – 如果你真的需要一个可变序列,请使用IListICollection接口或者返回一个具体的Collection

另外我建议您阅读被Eric Lippert 认为有害的arrays :

前几天我从编程语言教科书的作者那里得到了一个道德问题,请求我就是否应该教初学程序员如何使用数组提出意见。

我没有真正回答这个问题,而是给了他一长串我对arrays的看法,我如何使用数组,我们希望将来如何使用数组,等等。 这有点长,但像Pascal一样,我没有时间缩短它。

首先让我说一下你什么时候不应该使用数组,然后对现代编程的未来以及数组在即将到来的世界中的作用进行更多的哲学思考。

对于索引的属性集合(以及索引具有必要的语义含义),您应该使用ReadOnlyCollection (只读)或IList (读/写)。 它是最灵活和最富有表现力的。 对于非索引集合,请使用IEnumerable (只读)或ICollection (读/写)。

方法参数应该使用IEnumerable除非它们1)需要向集合添加/删除项( ICollection )或2)需要索引以用于必要的语义目的( IList )。 如果该方法可以从索引可用性(例如排序例程)中受益,则在实现失败时,它总是可以as IList.ToList()

我更喜欢IEnumerable,因为它是最高级别的界面,让最终用户有机会按照自己的意愿重新投射。 即使这可以为用户提供最小的function(基本上只是枚举),但仍然足以覆盖几乎任何需要,特别是扩展方法,ToArray(),ToList()等。

我在编写最有用的代码方面考虑到了这一点:代码可以做得更多。

换句话说,这意味着我喜欢接受最弱的接口作为方法参数,因为这使我的代码在更多地方有用。 在这种情况下,这是一个IEnumerable 。 有一个arrays? 你可以打电话给我的方法。 有一个清单? 你可以打电话给我的方法。 有一个迭代器块? 你明白了。

这也意味着我喜欢我的方法来返回方便的最强接口,因此依赖于该方法的代码可以轻松地做更多。 在这种情况下,那将是IList 。 请注意,这并不意味着我将构建一个列表,以便我可以返回它。 它只是意味着如果我已经有一些实现IList ,我也可以使用它。

请注意,关于返回类型,我有点不同寻常。 更典型的方法是从方法中返回较弱的类型,以避免将自己锁定到特定的实现中。

IEnumerable 对于延迟评估的迭代非常有用,尤其是在使用方法链接的场景中。

但作为典型数据访问层的返回类型,Count属性通常很有用,我宁愿返回带有Count属性的ICollection ,或者如果我认为典型的消费者想要使用的话,则返回IList 索引。

这也表明调用者实际上已经实现了该集合。 因此调用者可以遍历返回的集合,而不会从数据访问层获得exception。 这很重要。 例如,服务可以从返回的集合生成流(例如,SOAP)。 如果由于延迟评估的迭代而在生成流时从数据访问层抛出exception,则可能会很麻烦,因为在抛出exception时已经部分写入了输出流。

由于Linq扩展方法被添加到IEnumerable ,我发现我对其他接口的使用已大大减少; 可能在80%左右。 我过去常常使用List ,因为它有接受委托进行懒惰评估的方法,如Find,FindAll,ForEach等。 由于可以通过System.Linq的扩展获得,我已经用IEnumerable 引用替换了所有这些引用。

我不会使用数组,它是一种允许修改但没有添加/删除的类型…有点像最糟糕的包。 如果我想允许修改,那么我会使用支持添加/删除的类型。

当你想要阻止修改时,你已经包装它/复制它,所以我没有看到IEnumerable或ReadOnlyCollection有什么问题。 我会跟后面的…我不喜欢IEnumerable的东西就是它的懒惰本质,但是当你使用预加载的数据只是为了包装它时,调用与它一起使用的代码往往会假设加载数据或有额外的“不必要”行:( …在变化期间可能会得到丑陋的结果。