我们应该在设计公共API时避免使用IEnumerable作为输入吗?

时间:2012-07-19 18:44:14

标签: c# api ienumerable lazy-evaluation

考虑以下示例。

假设我开发了一个将由第三方使用的库。该库有一个接受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,还是在需要时我们是否应该在安全的上下文中允许和枚举它,因为它可能会引发异常。

5 个答案:

答案 0 :(得分:3)

根据Krzysztoc Cwalina和Brad Adams的框架设计指南一书。

http://blogs.msdn.com/b/kcwalina/archive/2008/01/03/frameworkdesignguidelines2ndedition.aspx

建议您这样做......

第252页的摘录(8.3.1)

  

收藏参数

     

请尽可能使用最不专业的类型作为参数类型。最   将集合作为参数的成员使用IEnumerable   接口

 public void PrintNames(IEnumerable<string> names) {
      foreach(string name in names) {
          Console.WriteLine(name);
      } }
  

使用ICollection或ICollection作为参数来避免   访问Count属性。

     

而是动态地考虑IEnumerable或IEnumerable   检查对象是否实现ICollection或ICollection

顺便说一句,这是一本很棒的书,值得一读,目前真正享受它。 附:写这篇文章的人帮助编写了.NET框架

答案 1 :(得分:1)

就我个人而言,我没有发现从您的方法中引发异常的问题 此 是一个错误,您的API用户应该知道它。

答案 2 :(得分:1)

您客户的代码错误的事实不是您的问题。如果他们希望他们的程序能够工作,请告诉他们停止编写错误的代码。你不能保护他们免受这些查询被懒惰评估的事实,他们显然不知道这一点。

您的客户有责任处理枚举集合时发生的事情,如果他们已经通过了懒惰评估的集合,那么他们的责任确保这个惰性求值不能抛出(如果这些是您定义的参数)。打破这个不变量就是他们的问题。

答案 3 :(得分:0)

是的,最好在库中使用最抽象的接口作为参数。 您可以通过这种方式为这些项目定义合同。

如果你只需要能够枚举一个集合,那么IEnumerable是一个非常好的选择。

如果您希望能够添加,索引等项目,IList是正确的选择。

答案 4 :(得分:0)

作为旁注,这里的问题不在于您的API采用IEnumerable。实际问题是.Select调用。延迟评估是LINQ的一个特性,而不是IEnumerable接口的一个特性。

所以是的,您应该将IEnumerable作为参数。资源的加载和管理完全在客户的控制之下,而不是您的。仅仅因为客户端可能会传递IEnumerable的某些实现,这可能导致延迟报告异常,这不足以牺牲API的可维护性和灵活性。