在我们的代码库中,我们有一个数据访问层,提供类似于以下的方法:
public IEnumerable<Foo> GetFoos()
{
var collection = new Collection<Foo>();
// ...call db
while (read results)
{
collection.Add(item);
}
return collection;
}
因此,当方法签名返回IEnumerable时,它会创建一个Collection(已经枚举)。期望IEnumerable的这个方法的消费者是否会实际调用GetEnumerator(),或者他们实际上是否会在不知不觉中使用Collection?
IEnumerable<Foo> myFoos = GetFoos();
// will .Any() cause enumeration even though it's a Collection under the hood?
if (myFoos != null && myFoos.Any())
{
// ...
}
答案 0 :(得分:4)
是的,它始终会调用GetEnumerator()
。
我们可以通过检查the implementation of Any()
on ReferenceSource.microsoft.com来验证这一点:
public static bool Any<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return true;
}
return false;
}
如果您担心性能,但仍希望限制消费者更改集合的能力,您可以考虑返回IReadOnlyCollection<T>
。这实际上只是IEnumerable<T>
和Count
属性。然后,消费者可以检查Count
属性,但除了枚举它之外不能做任何事情。 (除非他们开始做一些讨厌的事情,比如将它投射到原始集合类型......)
答案 1 :(得分:1)
如果您想在这种情况下避免集合的枚举,您可以执行以下操作:
ICollection col = myFoos as ICollection;
bool hasItems = false;
if (col != null && col.Count > 0)
{
hasItems = true;
}
else
{
hasItems = (myFoos != null && myFoos.Any());
}
但是这可能比仅仅调用.Any()
有更多的开销 - 检查.Count
带来的任何节省可能会被检查myFoos
是ICollection
的成本所抵消加载它。这样做的唯一好处是,如果你想对对象进行操作,就好像它是ICollection
一样。您的另一个选择就是让GetFoos
函数返回ICollection
开头...