这两个linq实现有什么区别?

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 语句,方法是迭代器生成器.

Mat*_*hen 5

破碎的代码是一个单一的方法,实际上是一个迭代器生成器.这意味着它最初只返回状态机而不做任何事情.只有当调用代码调用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)

在你开始迭代之前,破坏的版本不会给你一个错误.