代码之家  ›  专栏  ›  技术社区  ›  Guilherme

对count()等方法的连续调用是否重新枚举了IEnumerable?

  •  1
  • Guilherme  · 技术社区  · 7 年前

    我对IEnumerable和它的延迟执行行为有点困惑。

    假设我有以下内容 IEnumerable<T> :

    IEnumerable<Foo> enumerable = GetFoos();
    

    如果我这样做会发生什么:

    int count = enumerable.Count();
    count = enumerable.Count();
    

    我可以想到三种可能性:

    a)将再次枚举集合

    b)将缓存第二次使用的结果(如延迟加载)

    c)取决于 GetFoos() 方法及其如何实现IEnumerable接口

    哪一个是正确的?此外,如果 c 是正确的,使用创建的IEnumerable yield return ?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Freggar    7 年前

    referencesourcecode .Count<T>(this IEnumerable<T>) 扩展(简化):

    免责声明 你应该 依赖于那个实现,也不要期望实现总是以某种方式做一些事情。

    public static int Count<TSource>(this IEnumerable<TSource> source) {
        ICollection<TSource> collectionoft = source as ICollection<TSource>;
        if (collectionoft != null) return collectionoft.Count;
        ICollection collection = source as ICollection;
        if (collection != null) return collection.Count;
        int count = 0;
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            while (e.MoveNext()) count++;
        }
        return count;
    }
    

    所以答案是 (c) ,它将取决于基础类型。如果不是那种类型 ICollection 这个 IEnumerable 将进行第二次评估(即 GetEnumerator 将调用并循环枚举器。

    所以使用时会发生什么 yield
    Count()

    private static IEnumerable<int> ConstantEnumerable()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
    
    private static int i = 0;
    private static IEnumerable<int> ChangingEnumerable()
    {
        if (i == 0)
        {
            yield return 1;
            i++;
        }
        else
        {
            yield return 2;
            yield return 3;
        }
    }
    
    public static void Main()
    {
        var constant = ConstantEnumerable();
        var changing = ChangingEnumerable();
        Console.WriteLine("Constant: {0}, {1}", constant.Count(), constant.Count());  // 3, 3
        Console.WriteLine("Changing: {0}, {1}", changing.Count(), changing.Count()); // 1, 2
    }
    
    推荐文章