В чем разница между этими двумя реализациями linq? - PullRequest
2 голосов
/ 13 января 2011

Я проходил Серия переиздания Джона Скита «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 заявления.

Ответы [ 2 ]

5 голосов
/ 13 января 2011

Неработающий код - это единственный метод, действительно итератор-генератор.Это означает, что изначально он просто возвращает конечный автомат, ничего не делая.Только когда вызывающий код вызывает MoveNext (вероятно, как часть цикла for-each), он выполняет все от начала до первого возврата-возврата.

С правильным кодомWhere является , а не итератором-генератором.Это означает, что он выполняет все сразу, как обычно.Только WhereImpl есть.Таким образом, проверка выполняется сразу же, но код WhereImpl вплоть до первого возврата дохода откладывается.

Так что если у вас есть что-то вроде:

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

сломанная версияне выдаст ошибку, пока вы не начнете выполнять итерации.

2 голосов
/ 13 января 2011

Я думаю, что Джон довольно хорошо объясняет это в своей статье, но это объяснение основано на том, что вы понимаете, как компилятор генерирует код при наличии оператора yield.По сути, происходит то, что компилятор генерирует итератор, который не вызывается (отложенное выполнение) до тех пор, пока не потребуется один из элементов итерации.Первоначальный метод содержит как код, который проверяет аргументы, так и код итерации.Компилятор связывает все это в итератор, который, помните, не вызывается, пока не понадобится первый элемент.Это означает, что проверка не произойдет, пока вы не попытаетесь получить доступ к одному из элементов перечислимого.

Разделив его на два метода, один из которых содержит проверку, а другой - блок итератора, он гарантирует, что проверка правильностикод запускается при создании итератора, а не при его выполнении.Это потому, что единственный код, связанный с итератором, это код второго метода;это единственный код, выполнение которого отложено.Код проверки выполняется во время создания итератора.

...