简体   繁体   English

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

[英]What is the difference between these two linq implementations?

I was going through Jon Skeet's Reimplemnting Linq to Objects series . 我正在阅读乔恩·斯基特(Jon Skeet)的《重新实现Linq to Objects》系列 In the implementation of where article, I found the following snippets, but I don't get what is the advantage that we are gettting by splitting the original method into two. where文章的实现中 ,我发现了以下代码片段,但是我没有获得将原始方法分为两部分的优势。

Original Method: 原始方法:

// 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; 
        } 
    } 
}

Refactored Method: 重构方法:

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; 
        } 
    } 
} 

Jon says - Its for eager validation and then defferring for the rest of the part. 乔恩说-它渴望进行验证,然后再进行其余部分的处理。 But, I don't get it. 但是,我不明白。

Could some one please explain it in a little more detail, whats the difference between these 2 functions and why will the validations be performed in one and not in the other eagerly? 请问有人可以更详细地解释一下,这两个功能之间有什么区别,为什么要在一个而不是另一个急切地执行验证?

Conclusion/Solution: 结论/解决方案:

I got confused due to my lack of understanding on which functions are determined to be iterator-generators. 由于对哪些功能确定为迭代器生成器缺乏了解,我感到困惑。 I assumed that, it is based on signature of a method like IEnumerable <T> . 我以为它是基于IEnumerable <T>类的方法的签名的。 But, based on the answers, now I get it, a method is an iterator-generator if it uses yield statements. 但是,基于答案,现在我明白了,如果方法使用yield语句,则它是迭代器-生成器。

The broken code is a single method, really an iterator-generator. 损坏的代码是单个方法,实际上是迭代器-生成器。 That means it initially just returns a state machine without doing anything. 这意味着它最初只是不做任何事情就返回状态机。 Only when the calling code calls MoveNext (likely as part of a for-each loop) does it execute everything from the beginning up to the first yield-return. 只有当调用代码调用MoveNext (可能作为for-each循环的一部分)时,它才执行从开始到第一次yield-turn的所有操作。

With the correct code, Where is not an iterator-generator. 使用正确的代码, Where 不是迭代器-生成器。 That means it executes everything immediately, like normal. 这意味着它会像正常情况一样立即执行所有操作。 Only WhereImpl is. 只有WhereImpl是。 So the validation is executed right away, but the WhereImpl code up to and including the first yield return is deferred. 因此,验证将立即执行,但是直到第一个收益率返回(包括第一个收益率返回)的WhereImpl代码都会被推迟。

So if you have something like: 所以如果你有类似的东西:

IEnumerable<int> evens = list.Where(null); // Correct code gives error here.
foreach(int i in evens) // Broken code gives it here.

the broken version won't give you an error until you start iterating. 损坏的版本在开始迭代之前不会给您错误。

I think Jon explains it pretty well in his article, but the explanation relies on you understanding how the compiler generates code when there is a yield statement. 我认为Jon在他的文章中对此进行了很好的解释,但是这种解释依赖于您理解在产生yield语句时编译器如何生成代码。 Essentially what happens is the compiler generates an iterator that doesn't get invoked (deferred execution) until one of the items from the iteration is required. 本质上发生的是编译器生成一个迭代器,该迭代器在需要迭代中的一项之前不会被调用(延迟执行)。 The initial method contains both the code that checks the arguments and the iteration code. 初始方法包含检查参数的代码和迭代代码。 The compiler bundles all of this up into the iterator which, remember, doesn't get invoked until the first item is needed. 编译器将所有这些都捆绑到迭代器中,记住,直到需要第一项之前,该迭代器才被调用。 This means that validation doesn't happen until you try to access one of the items in the enumerable. 这意味着除非您尝试访问枚举中的一项,否则验证不会发生。

By separating it into two methods, one containing the validation and one containing the iterator block, it ensures that the validation code gets run when the iterator is constructed, not when it is executed. 通过将其分为两种方法,一种包含验证,另一种包含迭代器块,可以确保在构造迭代器时而不是在执行迭代器时运行验证代码。 This is because the only code bundled into the iterator is the code in the second method; 这是因为绑定到迭代器中的唯一代码是第二种方法中的代码。 it's the only code whose execution is deferred. 这是唯一延迟执行的代码。 The validation code is executed at the time you create the iterator. 验证代码是在创建迭代器时执行的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM