Mah*_*aga 2 c# linq-to-objects where deferred-execution
我正在经历Jon Skeet的Reimplemnting Linq to Objects系列.在where文章的实现中,我发现了以下片段,但是我没有得到通过将原始方法分成两部分来获得的优势.
原始方法:
// Naive validation - broken!
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
foreach (TSource item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
Run Code Online (Sandbox Code Playgroud)
重构方法:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
return WhereImpl(source, predicate);
}
private static IEnumerable<TSource> WhereImpl<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
foreach (TSource item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Jon说 - 它用于热切的验证,然后对其余的部分进行deffer.但是,我不明白.
有人可以更详细地解释一下,这两个函数之间的区别是什么?为什么验证会在一个而不是另一个中执行?
结论/解决方案:
由于我对哪些函数被确定为迭代器生成器缺乏了解,我感到困惑.我认为,它基于IEnumerable等
<T>
方法的签名 .但是,基于答案,现在我得到它,如果它使用yield 语句,方法是迭代器生成器.
破碎的代码是一个单一的方法,实际上是一个迭代器生成器.这意味着它最初只返回状态机而不做任何事情.只有当调用代码调用MoveNext时(可能作为for-each循环的一部分),它才会执行从开始到第一次yield-return的所有操作.
有了正确的代码,Where
是不是一个迭代器发电机.这意味着它会像平常一样立即执行所有操作.只是WhereImpl
.因此,验证立即执行,但是WhereImpl
延迟了包括第一个收益率返回的代码.
所以如果你有类似的东西:
IEnumerable<int> evens = list.Where(null); // Correct code gives error here.
foreach(int i in evens) // Broken code gives it here.
Run Code Online (Sandbox Code Playgroud)
在你开始迭代之前,破坏的版本不会给你一个错误.