Чтобы понять 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>
являются Общими, они могут использоваться с любым типом. Однако для простоты я использовал int
в моих примерах для типа T
.
Это означает, что вы можете использовать один из типов возврата IEnumerator<ProductMixHeader>
или IEnumerable<ProductMixHeader>
(пользовательский класс, который вы упомянули в своем вопросе).
Тип List<ProductMixHeader>
не реализует ни один из этих интерфейсов, поэтому вы не можете использовать его таким образом. Но Пример 2.b) показывает, как вы можете создать из него список.
Если вы создаете список, добавляя .ToList()
, то подразумевается, что он создаст список всех элементов в памяти, тогда как IEnumerable
позволяет лениво создавать свои элементы - с точки зрения производительности, он означает, что элементы перечисляются как раз вовремя - как можно позже, но как только вы используете .ToList()
, все элементы создаются в памяти. LINQ пытается оптимизировать производительность таким образом за кулисами.
DotNetFiddle для всех примеров