我之前在小话设计模式(十五)迭代器模式中简单介绍过IEnumerable的写法,这里就以此为例。
继承自IEnumable(即可被枚举/迭代的类型)接口的Inventory类(接口方法GetEnumerator):
public class Inventory : IEnumerable
{
private List
继承自IEnumerator(即枚举/迭代器)接口的InventoryEnumerator类(接口方法MoveNext、Reset和接口属性current):
public class InventoryEnumerator : IEnumerator
{
private int _current = -1;
private Inventory _inventory;
public InventoryEnumerator(Inventory inventory)
{
_inventory = inventory;
}
public object Current
{
get {
return _inventory [_current];
}
}
public bool MoveNext()
{
_current++;
return _current < _inventory.Count;
}
public void Reset()
{
_current = -1;
}
}
这样就可以使用foreach来遍历Inventory:
Inventory inventory = new Inventory ();
inventory[0] = "stone";
inventory[1] = "twig";
inventory[2] = "log";
inventory[3] = "carrot";
foreach (object obj in inventory) {
Console.WriteLine (obj);
}
public IEnumerable CreateEnum()
{
for (int i = 0; i < 10; i++) {
yield return i;
}
}
foreach (var i in CreateEnum()) {
Console.WriteLine (i);
}
yield return "x";
如果你需要限定yield return的类型,那么就需要使用泛型IEnumerable
public IEnumerable CreateStringEnum()
{
yield return "pen";
yield return "apple";
yield return "apple pen";
yield return "pen";
yield return "pineapple";
yield return "pineapple pen";
}
使用:
foreach (var i in CreateStringEnum()) {
Console.WriteLine (i);
}
如果你需要在某种情况下终止循环,你需要使用yield break。
我们用一种很傻但是很直观的方法修改一下CreateStringEnum:
public IEnumerable CreateStringEnum(int enumMax)
{
if (enumMax <= 0) {
yield break;
}
yield return "pen";
if (enumMax <= 1) {
yield break;
}
yield return "apple";
if (enumMax <= 2) {
yield break;
}
yield return "apple pen";
if (enumMax <= 3) {
yield break;
}
yield return "pen";
if (enumMax <= 4) {
yield break;
}
yield return "pineapple";
if (enumMax <= 5) {
yield break;
}
yield return "pineapple pen";
if (enumMax <= 6) {
yield break;
}
}
使用:
foreach (var i in CreateStringEnum(3)) {
Console.WriteLine (i);
}
yield除了可以隐式生成IEnumable类型外,还可以隐式的生成IEnumerator类型。
例如,我们可以直接把前面的Inventory的GetEnumerator的方法修改成:
public IEnumerator GetEnumerator()
{
foreach (var item in _items) {
yield return item;
}
}
也是一样可以使用foreach遍历的。yield break的用法跟前面的方法类似,这里就不赘述了。
但是泛型版本的IEnumerator
public class StrInventory : IEnumerable
{
private List _items = new List();
public string this[int index]
{
get { return _items [index];}
set { _items.Insert (index, value);}
}
public int Count
{
get { return _items.Count;}
}
IEnumerator _enumerator;
public IEnumerator GetEnumerator()
{
// if (_enumerator == null) {
// _enumerator = new StrInventoryEnumerator (this);
// }
// _enumerator.Reset ();
// return _enumerator;
foreach (var item in _items) {
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator ();
}
}
public class StrInventoryEnumerator : IEnumerator
{
private int _current = -1;
private StrInventory _inventory;
public StrInventoryEnumerator(StrInventory inventory)
{
_inventory = inventory;
}
public string Current
{
get {
return _inventory [_current];
}
}
object IEnumerator.Current
{
get {
return this.Current;
}
}
public bool MoveNext()
{
_current++;
return _current < _inventory.Count;
}
public void Reset()
{
_current = -1;
}
public void Dispose()
{
}
}
需要注意一点:yield return的类型跟IEnumerable
Console.WriteLine (inventory.GetEnumerator().GetType().Name);
Console.WriteLine (CreateEnum().GetType().Name);
注:
1、使用yield关键字的迭代器方法的返回值必须是IEnumerable、IEnumerable
2、yield无法在匿名方法(参考C#语法小知识(十二)匿名方法与Lambda表达式)或包含不安全的块(参考unsafe关键字)的方法中使用。
3、try-catch块中不能使用yield return语句,但可以使用yield break语句。try-finally的try块中可以使用yield return语句或yield break语句,finally块中二者都不可以使用。如果foreach语句(迭代器方法之外)引发异常,则会执行迭代器方法中的finally块。
最后,如果你需要了解迭代器的相关理论,不妨再看一下小话设计模式(十五)迭代器模式。