好的,我希望整个社区能够帮助我们解决一段时间以来一直在进行的工作场所辩论。这与定义接受或返回某种类型列表的接口有关。有几种方法可以做到这一点:
public interface Foo
{
Bar[] Bars { get; }
IEnumerable<Bar> Bars { get; }
ICollection<Bar> Bars { get; }
IList<Bar> Bars { get; }
}
我自己的偏好是使用IEnumerable作为参数和数组来返回值:
public interface Foo
{
void Do(IEnumerable<Bar> bars);
Bar[] Bars { get; }
}
我对这种方法的论证是实现类可以直接从IEnumerable创建一个List,并简单地用List.ToArray()返回它。但是有些人认为应该返回IList而不是数组。我在这里遇到的问题是,现在你需要再次使用ReadOnlyCollection复制它,然后再返回。返回IEnumerable的选项对于客户端代码来说似乎很麻烦?
你喜欢/喜欢什么? (特别是关于将由组织外部的其他开发人员使用的库)
答案 0 :(得分:18)
我的偏好是IEnumerable<T>
。任何其他建议的接口都给出了允许消费者修改底层集合的外观。这几乎肯定不是你想要做的,因为它允许消费者默默地修改内部集合。
另一个好的恕我直言,是ReadOnlyCollection<T>
。它允许所有有趣的.Count和Indexer属性,并明确地向消费者说“你无法修改我的数据”。
答案 1 :(得分:15)
我不返回数组 - 它们在创建API时确实是一种可怕的返回类型 - 如果你真的需要一个可变序列,请使用IList<T>
或ICollection<T>
接口或返回一个具体的{ {1}}而不是。
我还建议您阅读Eric Lippert撰写的Arrays considered somewhat harmful:
我从作者那里得到了一个道德问题 编程语言教科书 有一天请求我的意见 是否是初学者程序员 应该教会如何使用数组。
而不是实际回答这个问题 问题,我给了他一长串我的 关于数组的看法,我如何使用 数组,我们如何期待数组 在将来使用,等等。这个 有点长,但像Pascal,我 没时间把它缩短。
让我先说你什么时候 绝对不应该使用数组,和 然后更加富有哲理性 现代编程的未来和 阵列在即将到来的世界中的作用。
答案 2 :(得分:8)
对于索引的属性集合(以及索引具有必要的语义含义),您应该使用ReadOnlyCollection<T>
(只读)或IList<T>
(读/写)。这是最灵活和最富有表现力的。对于非索引集合,请使用IEnumerable<T>
(只读)或ICollection<T>
(读/写)。
方法参数应该使用IEnumerable<T>
,除非它们1)需要在集合中添加/删除项目(ICollection<T>
)或2)需要索引以用于必要的语义目的(IList<T>
)。如果该方法可以从索引可用性(例如排序例程)中受益,则在实现失败时,它总是可以使用as IList<T>
或.ToList()
。
答案 3 :(得分:1)
我更喜欢IEnumerable,因为它是最高级别的界面,让最终用户有机会按照自己的意愿重新投射。即使这可以为用户提供最小的功能(基本上只是枚举),但它仍然足以覆盖几乎任何需要,特别是使用扩展方法,ToArray(),ToList()等。
答案 4 :(得分:1)
我在编写最有用的代码方面考虑到了这一点:可以做更多的代码。
换句话说,这意味着我喜欢接受最弱的接口作为方法参数,因为这使我的代码在更多地方有用。在这种情况下,这是一个IEnumerable<T>
。有一个阵列?你可以打电话给我的方法。有一个清单?你可以打电话给我的方法。有一个迭代器块?你明白了。
这也意味着我喜欢我的方法来返回方便的最强接口,因此依赖于该方法的代码可以轻松地执行更多操作。在这种情况下,那将是IList<T>
。请注意,这并不意味着我将构建一个列表,以便我可以返回它。它只是意味着如果我已经有实现IList<T>
的一些,我也可以使用它。
请注意,关于返回类型,我有点不同寻常。更典型的方法是从方法中返回较弱的类型,以避免将自己锁定到特定的实现中。
答案 5 :(得分:0)
的IEnumerable&LT; T&GT;对于惰性求值迭代非常有用,尤其是在使用方法链接的场景中。
但作为典型数据访问层的返回类型,Count属性通常很有用,我宁愿返回ICollection&lt; T&gt;具有Count属性或可能是IList&lt; T&gt;如果我认为典型的消费者会想要使用索引器。
这也表明调用者实际上已经实现了该集合。因此调用者可以遍历返回的集合,而不会从数据访问层获得异常。这很重要。例如,服务可以从返回的集合生成流(例如,SOAP)。如果由于延迟评估的迭代而在生成流时从数据访问层抛出异常,则可能会很麻烦,因为在抛出异常时已经部分写入了输出流。
答案 6 :(得分:0)
由于Linq扩展方法被添加到IEnumerable&lt; T&gt;,我发现我对其他接口的使用已大大减少;可能在80%左右。我以前使用List&lt; T&gt;在宗教上,因为它有接受代表进行惰性评估的方法,如Find,FindAll,ForEach等。由于可以通过System.Linq的扩展获得,我已经用IEnumerable&lt; T&gt;替换了所有这些引用。引用。
答案 7 :(得分:0)
我不会使用数组,它的类型允许修改但没有添加/删除...有点像最糟糕的包。如果我想允许修改,那么我会使用支持添加/删除的类型。
当你想要阻止修改时,你已经包装它/复制它,所以我没有看到IEnumerable或ReadOnlyCollection有什么问题。我会跟后面的...我不喜欢IEnumerable的东西就是它的懒惰本质,但是当你使用预加载的数据只是为了包装它时,调用与它一起工作的代码往往假设加载数据或有额外的“不必要”行:( ...在变化期间可能会得到丑陋的结果。