Jul*_*lia 45
贝娄是我的回答,但也注意到Jon Skeet今天在他的博客上谈到了这个事实,即他对MSDN的"懒惰"含义并不完全正确,因为MSDN并不是很清楚他们使用时懒惰究竟是什么意思它在你有多懒惰?他的帖子有趣的阅读.
另外,维基百科假设应该为延迟评估维护三个规则,并且在MSDN中不尊重第三个点,这意味着如果GetEnumerator再次调用表达式将被多次评估(通过规范重置不会在使用yield关键字生成的枚举器对象上实现而且大多数linq目前使用它)
考虑一个功能
int Computation(int index)
Run Code Online (Sandbox Code Playgroud)
IEnumerable<int> GetComputation(int maxIndex)
{
var result = new int[maxIndex];
for(int i = 0; i < maxIndex; i++)
{
result[i] = Computation(i);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
Computation执行maxIndex次数GetEnumerator 返回枚举器的新实例,不再执行任何操作.MoveNext将存储在下一个Array单元格中的值放入其中的Current成员中IEnumerator.成本:大前期,枚举期间小(仅一份)
IEnumerable<int> GetComputation(int maxIndex)
{
var result = new int[maxIndex];
for(int i = 0; i < maxIndex; i++)
{
result[i] = Computation(i);
}
foreach(var value in result)
{
yield return value;
}
}
Run Code Online (Sandbox Code Playgroud)
IEnumerable被创建,并且参数(maxIndex)的副本存储在其中.GetEnumerator 返回枚举器的新实例,不再执行任何操作.MoveNext执行maxIndex 的第一次调用乘以compute方法,将结果存储在数组中并Current返回第一个值.MoveNext都会将Current一个值存入数组中.成本:没有任何前期,枚举开始时大,枚举时小(只有一个副本)
IEnumerable<int> GetComputation(int maxIndex)
{
for(int i = 0; i < maxIndex; i++)
{
yield return Computation(i);
}
}
Run Code Online (Sandbox Code Playgroud)
GetEnumerator 返回枚举器的新实例,不再执行任何操作.MoveNext执行一次Computation代码,将值放入Current并让调用者立即对结果执行操作.大多数linq使用延迟和延迟执行,但有些函数不能像排序那样.
成本:没有任何前期,在枚举期间中等(计算在那里执行)
MoveNext或IEnumerator创建实例时完成(因为IEnumerable它是在何时GetEnumerator被调用)MoveNext调用工作都会被调用,但之前没有.并行LINQ的做法略有不同,因为从调用者的角度来看,计算可以被认为是延迟/延迟,但是在内部,一旦枚举开始,一些元素的计算就会并行开始.结果是,如果下一个值已经存在,则立即得到它,否则您将不得不等待它.
您可以急切地评估延迟执行 IEnumerable 的一种方法是使用 linq 的 .ToArray() 函数将其简单地转换为数组。
var evaluated = enumerable.ToArray();
Run Code Online (Sandbox Code Playgroud)
这会强制对完整的可枚举进行评估,然后您就拥有了可以执行任何操作的数组。