处理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超载照顾。
如果目的是真的阻止多个枚举比由马克·格雷维尔的答案是阅读,但保持相同的语义,你可以简单地删除多余的Any
和First
电话,并与:
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
是可能的,但是首先要查看它是否真的存在问题。