Какова точная разница между возвратом экземпляра IEnumerable и оператором yield return в C # - PullRequest
9 голосов
/ 17 сентября 2010

В настоящее время я работаю с некоторыми библиотеками, применяющими отложенное выполнение с помощью итераторов. В некоторых ситуациях мне нужно просто «переслать» полученный итератор. То есть Мне нужно получить экземпляр IEnumerable<T> из вызванного метода и немедленно вернуть его.

Теперь мой вопрос: есть ли существенная разница между простым возвратом полученного IEnumerable<T> или повторным получением его через цикл?

IEnumerable<int> GetByReturn()
{
    return GetIterator(); // GetIterator() returns IEnumerable<int>
}
// or:
IEnumerable<int> GetByReYielding()
{
    for(var item in GetIterator()) // GetIterator() returns IEnumerable<int>
    {
        yield return item;
    }
}

Ответы [ 5 ]

4 голосов
/ 17 сентября 2010

Возможно, стоит прочитать статью Джона Скита об итераторах C #. Это довольно информативно.

http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx

3 голосов
/ 17 сентября 2010

Они разные.Например, если GetIterator() объявлен как:

IEnumerable<int> GetIterator() {
    List<int> result = new List<int>();
    for(int i = 0; i < 1000; i++) {
        result.Add(i);
    }
    return result;
}

Если вы не выполняете повторное получение, GetIterator() и цикл выполняются немедленно.Поэтому ответ зависит от того, как вы реализуете GetIterator().Если вы уверены, что GetIterator() будет уступать, значит, нет смысла возвращать его.

1 голос
/ 17 сентября 2010

Между ними нет никакой существенной разницы (кроме производительности , может * ), поскольку вы ничего не делаете с перечислителем из GetIterator(). Было бы разумно вернуть результат, если вы собираетесь что-то делать с перечислителем, например фильтровать его.

1 голос
/ 17 сентября 2010

Я не вижу никакой существенной разницы, кроме вздутия кода.

0 голосов
/ 13 ноября 2010

Есть существенная разница.

Выполнение GetByReYielding () будет выполняться отложенным образом (так как это блок итератора). Если вы использовали параметр в GetByReturn () или GetByReYielding () и проверили этот параметр во время выполнения на нулевое значение (или сделали какую-либо другую проверку), эта проверка будет выполнена немедленно при вызове GetByReturn (), но не сразу при вызове GetByReYielding () ! Проверка в GetByReYielding () будет выполняться отложенным способом, когда результат повторяется. - Это часто, ну, «слишком поздно». Смотрите здесь:

// Checks parameters early. - Fine. The passed argument will be checked directly when
// GetByReturn() is called.
IEnumerable<int> GetByReturn(IEnumerable<int> sequence)
{
    if(null == sequence)
    {
        throw new ArgumentNullException("sequence");
    }

    return GetIterator();
}
// Checks parameters in a deferred manner. - Possibly not desired, it's "too" late. I.e.                 // when the    
// result is iterated somewhere in a completely different location in your code the 
// argument passed once will be checked.
IEnumerable<int> GetByReYielding(IEnumerable<int> sequence)
{
    if(null == sequence)
    {
        throw new ArgumentNullException("sequence");
    }

    for(var item in GetIterator()) 
    {
        yield return item;
    }
}

г. Skeet объяснил эту концепцию в http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx. Стандартные операторы запросов, представленные в .Net, используют неотложенные функции-оболочки (например, Where ()), которые проверяют параметры и затем вызывают основную функцию итератора (как я показал в моей реализации GetByReturn ()).

Надеюсь, это поможет.

...