4

在回答这个问题时, runefs建议“除非您有非常具体的原因使用 IList,否则您应该考虑使用 IEnumerable”。你使用哪个,为什么?

4

6 回答 6

12

IEnumberable<T>是只读的,您必须重建集合才能对其进行更改。另一方面,IList<T>是读写。因此,如果您期望对集合进行大量更改,请公开,IList<T>但如果可以安全地假设您不会修改它,请使用IEnumerable<T>.

于 2009-05-28T12:44:26.330 回答
7

始终使用提供您需要的功能的最严格的接口,因为这为您提供了以后更改实现的最大灵活性。所以,如果IEnumerable<T>足够了,那就使用它……如果你需要列表功能,使用IList<T>.

并且最好使用强类型的泛型版本。

于 2009-05-28T12:44:15.097 回答
4

我遵循的原则是我不久前读到的:

“消费最简单,暴露最复杂”

(我敢肯定这真的很常见,而且我引用错误,如果有人知道出处,请您发表评论或编辑...)

(编辑添加 - 好吧,几周后我在这里,我刚刚在完全不同的背景下遇到了这句话,它看起来像是从鲁棒性原则或 Postel 定律开始的 -

做事要保守;对你从别人那里接受的东西要自由。

最初的定义是用于互联网上的网络通信,但我确信我已经看到它重新用于在 OO 中定义类合同。)

基本上,如果您正在定义一种用于外部消费的方法,那么参数应该是最基本的类型,可以为您提供所需的功能 - 在您的示例中,这可能意味着采用 IEnumerable 而不是 IList。这使客户端代码在可以传递的内容方面具有最大的灵活性。另一方面,如果您要公开一个属性以供外部使用,那么请使用最复杂的类型(IList 或 ICollection 而不是 IEnumerable)这样做,因为这为客户端提供了最他们使用对象的方式的灵活性。


我在评论中发现我和 DrJokepu 之间的对话很有趣,但我也很欣赏这不应该是一个讨论论坛,所以我将编辑我的答案以进一步概述我选择逆势而上的原因建议您将其公开为 IList(实际上是一个 List,如您所见)。假设这是我们正在谈论的课程:

public class Example
{
    private List<int> _internal = new List<int>();

    public /*To be decided*/ External
    {
        get { return _internal; }
    }
}

所以首先让我们假设我们将External 暴露为一个IEnumerable。我在其他答案和评论中看到这样做的原因目前是:

  • IEnumerable 是只读的
  • IEnumerable 是事实上的标准
  • 它减少了耦合,因此您可以更改实现
  • 您不会公开实现细节

虽然 IEnumerable 只公开了不会使其成为只读的只读功能。您返回的真实类型很容易通过反射或简单地暂停调试器并查看,因此将其转换回 List<> 是微不足道的,这同样适用于下面评论中的 FileStream 示例。如果您试图保护该成员,那么假装它是其他东西不是这样做的方法。

我不相信这是事实上的标准。我在 .NET 3.5 库中找不到任何代码,Microsoft 可以选择返回具体集合并改为返回接口。由于 LINQ, IEnumerable在 3.5更常见,但那是因为它们不能公开更多派生类型,而不是因为他们不想这样做。

现在,减少耦合我同意 - 在一定程度上 - 基本上这个论点似乎是你告诉客户,虽然你返回的对象一个列表,但我希望你对待它是一个 IEnumerable,以防你决定更改内部代码稍后。忽略这个问题以及无论如何都会暴露真实类型的事实,它仍然留下“为什么停止在 IEnumerable?”的问题。如果您将其作为对象类型返回,那么您将有完全的自由将实现更改为任何东西!这意味着您必须已决定客户端代码需要多少功能,因此您要么正在编写客户端代码,要么该决定基于任意指标。

最后,如前所述,您可以假设实现细节始终在 .NET 中公开,尤其是在公开公开对象时。

所以,经过这么长时间的谩骂之后,还有一个问题——为什么要将它公开为 List<>?那么,为什么不呢?将其公开为 IEnumerable 并没有获得任何好处,那么为什么人为地限制客户端代码使用该对象的能力呢?想象一下,如果 Microsoft 决定 Winforms 控件的 Controls 集合应该显示为 IEnumerable 而不是 ControlCollection - 您将不再能够将其传递给需要 IList 的方法,处理任意索引处的项目,或者查看它是否包含特定控件,除非您强制转换它。Microsoft 不会真正获得任何收益,只会给您带来不便。

于 2009-05-28T12:50:36.630 回答
3

当我只需要枚举孩子时,我使用 IEnumerable。如果我碰巧需要 Count,我会使用 ICollection。不过,我尽量避免这种情况,因为它暴露了实现细节。

于 2009-05-28T12:43:58.593 回答
2

IList<T>确实是一个非常强大的界面。我更喜欢公开 -Collection<T>派生类型。这与 Jeffrey Richter 建议的做法非常一致(附近没有书,所以不能指定页码/章节号):方法应该接受最常见的类型作为参数,并返回最“派生”的类型为返回值。

于 2009-05-28T12:45:48.963 回答
1

如果只读集合通过属性公开,那么执行此操作的常规方法是将其公开为 ReadOnlyCollection(或其派生类),包装您拥有的任何内容。它向客户端公开了 IList 的全部功能,但它非常清楚地表明它是只读的。

于 2009-06-07T11:05:45.120 回答