我知道IEnumerable很懒,但我不明白为什么Enumerable.Range在这里被重复两次:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication
{
    class Program
    {
        static int GetOne(int i)
        {
            return i / 2;
        }

        static IEnumerable<int> GetAll(int n)
        {
            var items = Enumerable.Range(0, n).Select((i) =>
            {
                Console.WriteLine("getting item: " + i);
                return GetOne(i);
            });

            var data = items.Select(item => item * 2);

            // data.Count does NOT causes another re-iteration
            Console.WriteLine("first: items: " + data.Count());
            return data;
        }

        static void Main()
        {
            var data = GetAll(3);

            // data.Count DOES cause another re-iteration
            Console.WriteLine("second: items: " + data.Count());
            Console.ReadLine();
        }
    }
}

结果:
getting item: 0
getting item: 1
getting item: 2
first: items: 3
getting item: 0
getting item: 1
getting item: 2
second: items: 3

为什么它在“第一个”情况下不被重复,而在“第二个”情况下被重复?

最佳答案

您正在触发对Count的重新迭代(为了提供答案,需要对源代码进行完全迭代)。IEnumerable永远不会保留它的值,并且总是在需要时重新迭代。
除了像ArrayList<T>这样的东西之外,这并不是一个问题,但是当实现在一个查询上,或者在一个复杂的yield return结构或其他一些代码集(如Enumerable.Range)上时,它可能会变得昂贵。
这就是为什么resharper会做像警告您多个枚举这样的事情。
如果需要记住Count的结果,请使用变量。如果你想避免枚举一个昂贵的源代码,你倾向于做一些像var myCachedValues = myEnumerable.ToArray()这样的事情,然后转而迭代数组(从而保证只有一次迭代)。
如果你想走愚蠢的路线(就像我做的那样),你可以实现一个枚举器,在内部缓存列表中的东西,这样你就可以得到延迟执行的任何好处,也可以得到缓存至少迭代一次的好处。我叫它IRepeatable。我为此受到了同事们的严厉批评,但我很固执。

10-01 23:46
查看更多