处理IEnumerable可能的多次枚举的警告

在我的代码中需要使用IEnumerable<>几次,从而得到“ IEnumerable可能多个枚举”的Resharper错误。

示例代码:

 public List<object> Foo(IEnumerable<object> objects) { if (objects == null || !objects.Any()) throw new ArgumentException(); var firstObject = objects.First(); var list = DoSomeThing(firstObject); var secondList = DoSomeThingElse(objects); list.AddRange(secondList); return list; } 
  • 我可以将objects参数更改为List ,然后避免可能的多个枚举,但是我没有得到我可以处理的最高的对象。
  • 我可以做的另一件事是在方法的开始处将IEnumerable转换为List

  public List<object> Foo(IEnumerable<object> objects) { var objectList = objects.ToList(); // ... } 

但这只是尴尬

你会在这种情况下做什么?

IEnumerable作为一个参数的问题是它告诉调用者“我想列举这个”。 它没有告诉他们你想要枚举多less次。

我可以将对象参数更改为列表,然后避免可能的多个枚举,但是我没有得到我可以处理的最高的对象

采取最高目标的目标是高尚的,但是为太多的假设留下了空间。 你真的希望有人通过一个LINQ to SQL查询到这个方法,只是让你枚举它两次(每次获得可能不同的结果?)

这里缺less的语义是,一个调用者可能不需要时间来阅读方法的细节,可能会认为你只能迭代一次 – 所以他们会传给你一个昂贵的对象。 您的方法签名不表示任何方式。

通过将方法签名更改为IList / ICollection ,您至less可以更清楚地告诉调用者您的期望是什么,并且可以避免代价高昂的错误。

否则,大多数看这个方法的开发者可能会认为你只迭代一次。 如果采用IEnumerable非常重要,则应该考虑在方法开始时执行.ToList()

这是一个耻辱.NET没有一个接口是IEnumerable + Count + Indexer,没有添加/删除等方法,这是我怀疑会解决这个问题。

如果你的数据总是可重复的,也许不用担心。 但是,也可以将其展开 – 如果传入数据可能很大(例如,从磁盘/networking读取),则此function特别有用:

 if(objects == null) throw new ArgumentException(); using(var iter = objects.GetEnumerator()) { if(!iter.MoveNext()) throw new ArgumentException(); var firstObject = iter.Current; var list = DoSomeThing(firstObject); while(iter.MoveNext()) { list.Add(DoSomeThingElse(iter.Current)); } return list; } 

注意我稍微改变了DoSomethingElse的语义,但是这主要是展示展开的用法。 例如,您可以重新包装迭代器。 你也可以使它成为一个迭代器块,这可能是很好的; 那么就没有list – 你会yield return的项目,而不是添加到列表返回。

在这种情况下,我通常用IEnumerable和IList重载我的方法。

 public static IEnumerable<T> Method<T>( this IList<T> source ){... } public static IEnumerable<T> Method<T>( this IEnumerable<T> source ) { /*input checks on source parameter here*/ return Method( source.ToList() ); } 

我注意在调用IEnumerable将执行.ToList()的方法的摘要注释中解释。

程序员可以select更高级别的.ToList()如果多个操作被连接,然后调用IList重载或让我的IEnumerable超载照顾。

如果目的是真的阻止多个枚举比由马克·格雷维尔的答案是阅读,但保持相同的语义,你可以简单地删除多余的AnyFirst电话,并与:

 public List<object> Foo(IEnumerable<object> objects) { if (objects == null) throw new ArgumentNullException("objects"); var first = objects.FirstOrDefault(); if (first == null) throw new ArgumentException( "Empty enumerable not supported.", "objects"); var list = DoSomeThing(first); var secondList = DoSomeThingElse(objects); list.AddRange(secondList); return list; } 

请注意,这假定您的IEnumerable不是通用的,或者至less被限制为引用types。

首先,这个警告并不总是那么重要。 确定它不是一个性能瓶颈后,我通常禁用它。 这只是意味着IEnumerable被评估两次,除非evaluation本身需要很长时间,否则这通常不是一个问题。 即使这需要很长时间,在这种情况下,你只能在第一次使用一个元素。

在这种情况下,你甚至可以利用强大的linq扩展方法。

 var firstObject = objects.First(); return DoSomeThing(firstObject).Concat(DoSomeThingElse(objects).ToList(); 

在这种情况下,只能用一些麻烦来评估一次IEnumerable是可能的,但是首先要查看它是否真的存在问题。