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