设计公共API时我们应该避免IEnumerable作为输入吗?
考虑下面的例子。设计公共API时我们应该避免IEnumerable作为输入吗?
假设我开发了一个将由第三方使用的库。该库有一个方法,接受IEnumerable
,这很好,因为它从传递的集合的实现中抽象出来。但是,当我检索输入参数时会发生懒惰评估。
public interface IStorage<in T>
{
void Store(IEnumerable<T> values);
}
public class FileStorage<T> : IStorage<T>
{
public void Store(IEnumerable<T> values)
{
foreach (var value in values) { /*Evaluation of IEnumerable*/}
}
}
一些客户使用我的图书馆以下列方式:
IEnumerable<int> values;
try
{
values = new[] { "1", "2", "three" }.Select(int.Parse);
}
catch (Exception)
{
values = Enumerable.Empty<int>();
}
var storage = new FileStorage<int>();
storage.Store(values);
这将导致Store
方法,其中评价发生内部异常。如果我设计Store
方法采取List<T>
我肯定它是安全枚举集合的Ts。问题是我们应该在设计API时避免IEnumerable
,或者我们是否应该在需要时允许并枚举安全上下文,因为它可能会引发异常。
就我个人而言,我看不出您的方法中引发异常的问题。
这个是错误,你的API的用户应该知道它。
是的,在库中有最抽象的接口作为参数是一种很好的做法。 您以这种方式定义这些项目的合同。
如果您只需要枚举集合,那么IEnumerable是一个非常好的选择。
如果您希望能够添加,索引等项目,IList是正确的选择。
事实上,您的客户端的代码是越野车不是你的问题。如果他们希望他们的程序能够工作,请告诉他们停止编写错误代码。你无法保护他们免受这些查询被懒惰评估的事实,他们显然不知道这一点。
这是客户的责任来处理,当你枚举集合发生了什么,以及如果他们通过你懒洋洋地评估收集,那么它是他们责任确保这种懒惰的评价不能扔(如果这些是你定义的参数)。打破这个不变是他们的问题。
作为一个方面说明,这里的问题不在于您的API采用IEnumerable。实际的问题是.Select调用。延迟评估是LINQ的一项功能,而不是IEnumerable界面的功能。
所以是的,你应该把一个IEnumerable作为参数。资源的加载和管理完全由客户控制,而不是你的。仅仅因为客户端可能会传入IEnumerable的某些实现,这可能会导致延迟报告异常,因此不会牺牲API的可维护性和灵活性。
根据Krzysztoc Cwalina和Brad Adams编写的框架设计指南。
http://blogs.msdn.com/b/kcwalina/archive/2008/01/03/frameworkdesignguidelines2ndedition.aspx
它建议你做...
从252页(8.3.1)的提取
收集参数
DO用最少的专门类型的可能作为参数的类型。大多数 成员以收藏作为参数使用了IEnumerable接口
public void PrintNames(IEnumerable<string> names) {
foreach(string name in names) {
Console.WriteLine(name);
} }
避免使用的ICollection或ICollection的作为参数只是为了 访问Count属性。
而是考虑了IEnumerable或IEnumerable和动态 检查对象是否实现的ICollection或ICollection的
顺便提一下它的一个伟大的书和一个值得读,现在真的很享受它。 P.S.谁写了这家伙,帮助撰写了.NET框架
好,不过我可能需要做一些清理工作(如关闭数据库连接),所以我想知道有什么可以抛出异常 – nan 2012-07-19 18:53:33
@nan:一'using'块是处理这种情况的标准方法。正确使用它将确保任何异常都会导致您的连接关闭。 – Guvante 2012-07-19 18:56:01
它不仅仅是处理它的事实,我需要恢复一些异常状态,我需要了解它们。 – nan 2012-07-19 18:58:32