我最近在其他地方表达了我对此的看法*,但我认为值得进一步分析,所以我将其作为自己的问题发布。
假设我需要在程序中创建并传递容器。我可能对一种容器与另一种容器没有强烈的意见,至少在这个阶段,但我选择了一个;为了论证,我们假设我将使用 List<> 。
问题是:编写我的方法接受并返回高级接口(例如C#的 IEnumerable )是否更好?或者我应该编写方法来获取并传递我选择的特定容器类。
我应该选择哪些因素和标准来决定?什么样的程序可以从中受益?计算机语言会影响您的决定吗?性能?程序大小?个人风格?
(甚至重要吗?)
**(作业:找到它。但是在你寻找我自己之前请在这里发表你的答案,以免偏见你。)*
答案 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>
将表示序列。然后,如果您需要Add
或Remove
个项目(但仍然不需要按索引查找),请使用继承IContainer<T>
的{{1}},与您的其他代码完全互操作。
通过这种方式,可以非常清楚(仅从本地检查某些代码)到该代码能够对数据做些什么。
小程序需要较少的抽象,这是事实。但如果他们成功了,他们往往会成为大项目。如果他们首先采用简单的抽象,那就容易多了。
答案 4 :(得分:0)
这很重要,但正确的解决方案完全取决于使用情况。如果您只需要进行简单的枚举,那么一定要使用IEnumerable,这样您就可以通过任何实现者来访问您需要的功能。但是,如果您需要列表功能,并且您不希望必须创建列表的新实例,如果每次调用该方法时,通过的枚举不是列表,那么请使用列表。
答案 5 :(得分:0)