簡體   English   中英

這兩個linq實現之間有什么區別?

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

我正在閱讀喬恩·斯基特(Jon Skeet)的《重新實現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; 
        } 
    } 
}

重構方法:

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

喬恩說-它渴望進行驗證,然后再進行其余部分的處理。 但是,我不明白。

請問有人可以更詳細地解釋一下,這兩個功能之間有什么區別,為什么要在一個而不是另一個急切地執行驗證?

結論/解決方案:

由於對哪些功能確定為迭代器生成器缺乏了解,我感到困惑。 我以為它是基於IEnumerable <T>類的方法的簽名的。 但是,基於答案,現在我明白了,如果方法使用yield語句,則它是迭代器-生成器。

損壞的代碼是單個方法,實際上是迭代器-生成器。 這意味着它最初只是不做任何事情就返回狀態機。 只有當調用代碼調用MoveNext (可能作為for-each循環的一部分)時,它才執行從開始到第一次yield-turn的所有操作。

使用正確的代碼, Where 不是迭代器-生成器。 這意味着它會像正常情況一樣立即執行所有操作。 只有WhereImpl是。 因此,驗證將立即執行,但是直到第一個收益率返回(包括第一個收益率返回)的WhereImpl代碼都會被推遲。

所以如果你有類似的東西:

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

損壞的版本在開始迭代之前不會給您錯誤。

我認為Jon在他的文章中對此進行了很好的解釋,但是這種解釋依賴於您理解在產生yield語句時編譯器如何生成代碼。 本質上發生的是編譯器生成一個迭代器,該迭代器在需要迭代中的一項之前不會被調用(延遲執行)。 初始方法包含檢查參數的代碼和迭代代碼。 編譯器將所有這些都捆綁到迭代器中,記住,直到需要第一項之前,該迭代器才被調用。 這意味着除非您嘗試訪問枚舉中的一項,否則驗證不會發生。

通過將其分為兩種方法,一種包含驗證,另一種包含迭代器塊,可以確保在構造迭代器時而不是在執行迭代器時運行驗證代碼。 這是因為綁定到迭代器中的唯一代碼是第二種方法中的代碼。 這是唯一延遲執行的代碼。 驗證代碼是在創建迭代器時執行的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM