Подтвердить что ты не робот

Некоторые помогают понять "доходность",

В моих вечных поисках, чтобы сосать меньше, я пытаюсь понять инструкцию yield, но я все время сталкиваюсь с той же ошибкой.

Тело [someMethod] не может быть блоком итератора, потому что "System.Collections.Generic.List <AClass> 'не является типом интерфейса итератора.

Это код, где я застрял:

foreach (XElement header in headersXml.Root.Elements()){
    yield return (ParseHeader(header));                
}

Что я делаю неправильно? Могу ли я использовать доход в итераторе? Тогда какой смысл? В этом примере сказано, что List<ProductMixHeader> не является типом интерфейса итератора. ProductMixHeader - это настраиваемый класс, но я полагаю, что List - это тип интерфейса итератора, no?

- Edit -
Спасибо за все быстрые ответы. Я знаю, что этот вопрос не все, что новые и те же ресурсы продолжают появляться.
Оказалось, я думал, что могу вернуть List<AClass> как возвращаемый тип, но поскольку List<T> не ленив, он не может. Изменение моего типа возврата на IEnumerable<T> решило проблему: D

Несколько связанный вопрос (не стоит открывать новый поток): стоит ли IEnumerable<T> использовать тип возвращаемого типа, если я уверен, что в 99% случаев я собираюсь идти. ToList() в любом случае? Каковы будут последствия для производительности?

4b9b3361

Ответ 1

Метод, использующий return return, должен быть объявлен как возвращающий один из следующих двух интерфейсов:

IEnumerable<SomethingAppropriate>
IEnumerator<SomethingApropriate>

(спасибо Jon и Marc для указания IEnumerator)

Пример:

public IEnumerable<AClass> YourMethod()
{
    foreach (XElement header in headersXml.Root.Elements())
    {
        yield return (ParseHeader(header));                
    }
}

yield - ленивый производитель данных, только создающий другой элемент после первого поиска, тогда как возврат списка возвращает все за один раз.

Итак, есть разница, и вам нужно правильно объявить метод.

Для получения дополнительной информации прочитайте Jon answer here, который содержит очень полезные ссылки.

Ответ 2

Это сложная тема. Вкратце, это простой способ внедрения IEnumerable и его друзей. Компилятор создает вам машину состояний, преобразуя параметры и локальные переменные в переменные экземпляра в новом классе. Сложные вещи.

У меня есть несколько ресурсов по этому поводу:

Ответ 3

"yield" создает блок итератора - класс, сгенерированный компилятором, который может реализовать либо IEnumerable[<T>], либо IEnumerator[<T>]. У Jon Skeet есть очень хорошее (и бесплатное) обсуждение этого в главе 6 С# в глубине.

Но в принципе - для использования "yield" ваш метод должен возвращать IEnumerable[<T>] или IEnumerator[<T>]. В этом случае:

public IEnumerable<AClass> SomeMethod() {
    // ...
    foreach (XElement header in headersXml.Root.Elements()){
        yield return (ParseHeader(header));                
    }
}

Ответ 4

Список реализует Ienumerable.

Вот пример, который может пролить свет на то, что вы пытаетесь узнать. Я написал это около 6 месяцев

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YieldReturnTest
{
    public class PrimeFinder
    {
        private Boolean isPrime(int integer)
        {
            if (0 == integer)
                return false;

            if (3 > integer)
                return true;

            for (int i = 2; i < integer; i++)
            {
                if (0 == integer % i)
                    return false;
            }
            return true;
        }

        public IEnumerable<int> FindPrimes()
        {
            int i;

            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    yield return i;
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PrimeFinder primes = new PrimeFinder();

            foreach (int i in primes.FindPrimes())
            {
                Console.WriteLine(i);
                Console.ReadLine();
            }

            Console.ReadLine();
            Console.ReadLine();
        }
    }
}

Ответ 5

Я настоятельно рекомендую использовать Reflector, чтобы посмотреть, что yield действительно для вас. Вы сможете увидеть полный код класса, который генерирует компилятор для вас при использовании урожая, и я обнаружил, что люди понимают эту концепцию гораздо быстрее, когда видят результат низкого уровня (ну, уровень, я думаю).

Ответ 6

Чтобы понять yield, вам нужно понять, когда использовать IEnumerator и IEnumerable (потому что вы должны использовать любой из них). Следующие примеры помогут вам понять разницу.

Сначала рассмотрим следующий класс, он реализует два метода: один возвращает IEnumerator<int>, один возвращает IEnumerable<int>. Я покажу вам, что есть большая разница в использовании, хотя код из двух методов выглядит аналогичным:

// 2 iterators, one as IEnumerator, one as IEnumerable
public class Iterator
{
    public static IEnumerator<int> IterateOne(Func<int, bool> condition)
    {
        for(var i=1; condition(i); i++) { yield return i; }     
    }
    public static IEnumerable<int> IterateAll(Func<int, bool> condition)
    {
        for(var i=1; condition(i); i++) { yield return i; }     
    }
}

Теперь, если вы используете IterateOne, вы можете сделать следующее:

    // 1. Using IEnumerator allows to get item by item
    var i=Iterator.IterateOne(x => true); // iterate endless
    // 1.a) get item by item
    i.MoveNext(); Console.WriteLine(i.Current);
    i.MoveNext(); Console.WriteLine(i.Current);
    // 1.b) loop until 100
    int j; while (i.MoveNext() && (j=i.Current)<=100) { Console.WriteLine(j); }

1.a) печатает:

1
2

1.b) печатает:

3
4
 ...
100

потому что он продолжает считать сразу после выполнения 1.a).

Вы можете видеть, что вы можете продвигать элемент по элементу с помощью MoveNext().


Напротив, IterateAll позволяет использовать foreach, а также LINQ для большего комфорта:

    // 2. Using IEnumerable makes looping and LINQ easier   
    var k=Iterator.IterateAll(x => x<100); // limit iterator to 100
    // 2.a) Use a foreach loop
    foreach(var x in k){ Console.WriteLine(x); } // loop
    // 2.b) LINQ: take 101..200 of endless iteration
    var lst=Iterator.IterateAll(x=>true).Skip(100).Take(100).ToList(); // LINQ: take items
    foreach(var x in lst){ Console.WriteLine(x); } // output list

2.a) печатает:

1
2
 ...
99

2.b) печатает:

101
102
 ...
200


Примечание: Поскольку IEnumerator<T> и IEnumerable<T> являются Generics, они могут использоваться с любым типом. Однако для простоты я использовал int в моих примерах для типа T.

Это означает, что вы можете использовать один из типов возврата IEnumerator<ProductMixHeader> или IEnumerable<ProductMixHeader> (пользовательский класс, который вы упомянули в своем вопросе).

Тип List<ProductMixHeader> не реализует ни один из этих интерфейсов, поэтому вы не можете использовать его таким образом. Но Пример 2.b) показывает, как вы можете создать список из него.

Ответ 7

Как выглядит метод, которым вы пользуетесь? Я не думаю, что это можно использовать только в одном цикле.

Например...

public IEnumerable<string> GetValues() {
    foreach(string value in someArray) {
        if (value.StartsWith("A")) { yield return value; }
    }
}

Ответ 8

@Ответ от PI помог мне понять, почему и почему он используется. Один (основной) вариант использования для доходности находится в циклах "foreach" после ключевого слова "in", чтобы не возвращать полностью заполненный список. Вместо того, чтобы сразу возвращать полный список, в каждом цикле "foreach" возвращается только один элемент (следующий элемент). Таким образом, вы получите производительность с доходностью в таких случаях. Я переписал код @Ian P для лучшего понимания следующего:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YieldReturnTest
{
    public class PrimeFinder
    {
        private Boolean isPrime(int integer)
        {
            if (0 == integer)
                return false;

            if (3 > integer)
                return true;

            for (int i = 2; i < integer; i++)
            {
                if (0 == integer % i)
                    return false;
            }
            return true;
        }

        public IEnumerable<int> FindPrimesWithYield()
        {
            int i;

            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    yield return i;
                }
            }
        }

        public IEnumerable<int> FindPrimesWithoutYield()
        {
            var primes = new List<int>();
            int i;
            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    primes.Add(i);
                }
            }
            return primes;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PrimeFinder primes = new PrimeFinder();

            Console.WriteLine("Finding primes until 7 with yield...very fast...");
            foreach (int i in primes.FindPrimesWithYield()) // FindPrimesWithYield DOES NOT iterate over all integers at once, it returns item by item
            {
                if (i > 7)
                {
                    break;
                }
                Console.WriteLine(i);
                //Console.ReadLine();

            }

            Console.WriteLine("Finding primes until 7 without yield...be patient it will take lonkg time...");
            foreach (int i in primes.FindPrimesWithoutYield()) // FindPrimesWithoutYield DOES iterate over all integers at once, it returns the complete list of primes at once
            {
                if (i > 7)
                {
                    break;
                }
                Console.WriteLine(i);
                //Console.ReadLine();
            }

            Console.ReadLine();
            Console.ReadLine();
        }
    }
}