这会不止一次调用GetEnumerator()吗?

时间:2016-02-26 15:34:45

标签: c# linq ienumerable enumeration

在我们的代码库中,我们有一个数据访问层,提供类似于以下的方法:

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())
{
    // ...
}

2 个答案:

答案 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带来的任何节省可能会被检查myFoosICollection的成本所抵消加载它。这样做的唯一好处是,如果你想对对象进行操作,就好像它是ICollection一样。您的另一个选择就是让GetFoos函数返回ICollection开头...