要理解yield
,您需要了解何时使用IEnumerator
and 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
您使用LINQ语句以获得更大的舒适度:foreach
// 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
在示例中使用了 type T
。
这意味着,您可以使用其中一种返回类型IEnumerator<ProductMixHeader>
或IEnumerable<ProductMixHeader>
(您在问题中提到的自定义类)。
该类型List<ProductMixHeader>
没有实现这些接口中的任何一个,这就是您不能以这种方式使用它的原因。但是示例 2.b)展示了如何从中创建列表。
如果您通过附加来创建列表,.ToList()
则意味着它将创建内存中所有元素的列表,而 anIEnumerable
允许延迟创建其元素 - 就性能而言,这意味着元素被及时枚举 -尽可能晚,但一旦你使用.ToList()
,那么所有元素都会在内存中创建。LINQ 试图在幕后以这种方式优化性能。
所有示例的 DotNetFiddle