推迟执行和急切的评估
你可以给我一个例子,用C#进行急切的评估吗?
我从MSDN读取,推迟执行LINQ可以执行懒惰或渴望评估。 我可以在互联网上find延迟执行与懒惰评估的例子,但我找不到任何示例延迟执行与急切的评价。
而且,延期执行与懒惰评估有什么不同呢? 在我看来,两人看起来都一样。 你能提供一个例子吗?
贝娄是我的回答,但也注意到,Jon Skeet今天在他的博客上谈到了这个事实,即他并不完全符合MSDN的“懒惰”的含义,因为MSDN并不清楚他们使用它在你只是多么懒惰? 他的职位做了一个有趣的阅读。
另外, 维基百科认为应该为延迟评估维护三个规则,第三点在MSDN中不受尊重,因为如果再次调用GetEnumerator
,expression式将被多次评估(由于规范Reset没有对使用yield
关键字和linq的大部分目前使用它)
考虑一个function
int Computation(int index)
立即执行
IEnumerable<int> GetComputation(int maxIndex) { var result = new int[maxIndex]; for(int i = 0; i < maxIndex; i++) { result[i] = Computation(i); } return result; }
- 当函数被调用时,
Computation
被执行的maxIndex
次数 -
GetEnumerator
返回一个新的枚举数实例。 - 对
MoveNext
每次调用都将存储在下一个数组单元格中的值存储在IEnumerator
的Current
成员中,就这些了。
成本 :先期大,枚举小(只有副本)
推迟但急切的执行
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; } }
- 当函数被调用时,会创build一个自动生成的类的实例(在规范中称为“可枚举对象”),实现
IEnumerable
被创build并且参数的一个副本(maxIndex
)被存储在其中。 -
GetEnumerator
返回一个新的枚举数实例。 - 对
MoveNext
的第一次调用执行计算方法的maxIndex次,将结果存储在数组中,Current
将返回第一个值。 - 随后对
MoveNext
每次MoveNext
都会将Current
存储在数组中的值存入Current
值。
成本 :没什么前期的,枚举开始时大,枚举时小(只是一个副本)
延期和懒惰的执行
IEnumerable<int> GetComputation(int maxIndex) { for(int i = 0; i < maxIndex; i++) { yield return Computation(i); } }
- 当这个函数被调用的时候,和懒惰的执行情况一样。
-
GetEnumerator
返回一个新的枚举数实例。 - 对
MoveNext
每次调用都会执行一次Computation
代码,将该值Current
并让调用者立即对结果进行操作。
linq的大部分使用延迟和懒惰执行,但一些函数不能这样sorting。
成本 :没有先期的,枚举中的中等(计算在那里执行)
总结
- 立即表示计算/执行在函数中完成,并且一旦函数返回完成。 (像大多数C#代码那样完全热切的评估)
- Deferred / Eager意味着大部分工作将在第一个
MoveNext
上完成,或者在创buildIEnumerator
实例时完成(对于IEnumerable
它是在调用GetEnumerator
时候) - 延迟/ 延迟意味着每次
MoveNext
都会完成工作,但之前没有任何操作。
并行LINQ的做法有点不同,因为从调用者的angular度来看,计算可以被认为是延迟/懒惰,但是一旦枚举开始,内部计算一些元素就会并行开始。 结果是,如果下一个值已经在那里,你立即得到它,否则你将不得不等待它。
您可以急切地评估延迟执行的IEnumerable的一种方法是使用linq的.ToArray()函数将其简单地转换为数组。
var evaluated = enumerable.ToArray();
这迫使评估完整的枚举,然后你有数组,你可以做任何你想要的。