我应该使用像IEnumerable这样的接口,还是像List<>这样的具体类?

时间:2009-03-05 05:19:44

标签: language-agnostic oop polymorphism

我最近在其他地方表达了我对此的看法*,但我认为值得进一步分析,所以我将其作为自己的问题发布。

假设我需要在程序中创建并传递容器。我可能对一种容器与另一种容器没有强烈的意见,至少在这个阶段,但我选择了一个;为了论证,我们假设我将使用 List<>

问题是:编写我的方法接受并返回高级接口(例如C#的 IEnumerable )是否更好?或者我应该编写方法来获取并传递我选择的特定容器类。

我应该选择哪些因素和标准来决定?什么样的程序可以从中受益?计算机语言会影响您的决定吗?性能?程序大小?个人风格?

(甚至重要吗?)

**(作业:找到它。但是在你寻找我自己之前请在这里发表你的答案,以免偏见你。)*

6 个答案:

答案 0 :(得分:16)

您的方法应始终接受执行其功能所需的最不具体类型。如果您的方法需要枚举,请接受IEnumerable。如果它需要IList<> - 具体的事情,根据定义,你必须给它一个IList<>

答案 1 :(得分:5)

唯一会影响您决定的是您计划如何使用该参数。如果您只是迭代它,请使用IEnumerable<T>。如果您要访问索引成员(例如var x = list[3])或以任何方式修改列表(例如list.Add(x)),请使用ICollection<T>IList<T>

答案 2 :(得分:3)

总有一个权衡。一般的经验法则是尽可能地将事物声明为层次结构。因此,如果你只需要访问IEnumerable中的方法,那就是你应该使用的。

最近SO问题的另一个例子是采用文件名而不是文件*(或文件描述符)的C API。在那里,文件名严格限制了可以传入的东西的疮(有很多东西你可以传入文件描述符,但只有一个有文件名)。

一旦你必须开始施法,你要么太高了要么你应该制作第二种采用更具体类型的方法。

我能想到的唯一例外是速度是绝对必须的,而且你不想花费虚拟方法调用。声明特定类型会消除虚函数的开销(取决于语言/环境/实现,但作为可能正确的一般声明)。

答案 3 :(得分:3)

与我讨论提出了这个问题,所以Euro Micelli已经知道了我的答案,但现在就是这样! :)

我认为Linq to Objects已经为这个问题提供了很好的答案。通过使用最简单的接口来实现一系列项目,它为您实现序列提供了最大的灵活性,允许延迟生成,在不牺牲性能的情况下提高生产力(不是真正意义上的)。

过早抽象确实有成本 - 但主要是发现/发明新抽象的成本。但是如果你已经提供了非常好的,那么你就不会利用它们而疯狂,这就是通用集合界面为你提供的。

有些人会告诉您,将类中的所有数据公开后会“更容易”,以防您需要访问它。同样地,Euro建议最好使用丰富的接口来容器,例如IList<T>(甚至是具体的类List<T>),然后再清理乱七八糟。

但我认为,就像隐藏您不想访问的类的数据成员更好,允许您稍后轻松修改该类的实现,因此您应该使用最简单的可用接口引用一系列项目。在实践中更容易从暴露简单和基本的东西开始,然后在以后“放松”它,而不是从松散的东西开始并努力对它施加秩序。

因此假设IEnumerable<T>将表示序列。然后,如果您需要AddRemove个项目(但仍然不需要按索引查找),请使用继承IContainer<T>的{​​{1}},与您的其他代码完全互操作。

通过这种方式,可以非常清楚(仅从本地检查某些代码)到该代码能够对数据做些什么。

小程序需要较少的抽象,这是事实。但如果他们成功了,他们往往会成为大项目。如果他们首先采用简单的抽象,那就容易多了。

答案 4 :(得分:0)

这很重要,但正确的解决方案完全取决于使用情况。如果您只需要进行简单的枚举,那么一定要使用IEnumerable,这样您就可以通过任何实现者来访问您需要的功能。但是,如果您需要列表功能,并且您不希望必须创建列表的新实例,如果每次调用该方法时,通过的枚举不是列表,那么请使用列表。

答案 5 :(得分:0)

我回答了类似的C#问题here。我认为你应该总是提供最简单的合同,在我看来,在收藏的情况下,通常是IEnumerable Of T.

实现可以由内部BCL类型提供 - 无论是Set,Collection,List etcetera - 其所需成员都是您的类型公开的。

您的抽象类型始终可以继承简单的BCL类型,这些类型由您的具体类型实现。在我看来,这可以让您更容易遵守LSP