C# 迭代器与yield关键字
迭代器模式是设计模式的一种,因为其运用的普遍性,很多语言都有内嵌的原生支持
在.NET中,迭代器模式是通过IEnumerator、IEnumerable两个接口(有非泛型和泛型2种版本)来封装的
迭代器模式的一个重要方面是:不是一次返回所有数据,而是每次调用只返回一个元素
Array、IEnumerable和IEnumerator之间关系如下:
foreach遍历
① Array、集合容器类派生于IEnumerable接口类(该类中含有一个IEnumerator GetEnumerator()接口函数),Array、集合容器类实现了该函数,这使得foreach可对其进行遍历
注:不需要从IEnumerable派生,只要类实现了IEnumerator GetEnumerator()函数,就可以使用foreach遍历该类中的元素
② IEnumerator定义了Current属性来返回游标所在的元素,MoveNext方法移动到下一个元素(若有元素则返回true,若到达末尾则返回false),Reset方法是将游标重置到第一项的位置
int[] IntArray = new int[] { 1, 2, 3 }; foreach (int n in IntArray) { Console.WriteLine(n); } // 上面的foreach等价于下方的代码实现 IEnumerator e = IntArray.GetEnumerator(); try { while (e.MoveNext()) { Console.WriteLine((int)e.Current); } } finally { var disposable = e as IDisposable; if (disposable != null) { disposable.Dispose(); } }
在C#1.0中,创建IEnumerator枚举器需要写大量的代码。C#2.0添加了yield语句,让编译器自动创建IEnumerator枚举器。
yield return语句返回集合的一个元素,并移动到下一个元素上。yield break可停止迭代。
非泛型版本IEnumerator
class EnumeratorTest { public IEnumerator GetEnumerator() { yield return 100; yield return "Good"; } } EnumeratorTest eTest = new EnumeratorTest(); foreach (object o in eTest) { Console.WriteLine(o); }
编译后生成的代码如下:
class EnumeratorTest { public IEnumerator GetEnumerator() { return new GenerateEnumerator(0); } private sealed class GenerateEnumerator : IEnumerator<object>, IEnumerator, IDisposable { // Fields private int state; private object current; // Methods public GenerateEnumerator(int state) { this.state = state; } bool IEnumerator.MoveNext() { switch (this.state) { case 0: this.state = -1; this.current = 100; this.state = 1; return true; case 1: this.state = -1; this.current = "Good"; this.state = 2; return true; case 2: this.state = -1; break; } return false; } void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } // Properties object IEnumerator<object>.Current { get { return this.current; } } object IEnumerator.Current { get { return this.current; } } } } EnumeratorTest eTest = new EnumeratorTest(); IEnumerator e = eTest.GetEnumerator(); try { while (e.MoveNext()) { Console.WriteLine(e.Current); } } finally { var disposable = e as IDisposable; if (disposable != null) { disposable.Dispose(); } }
泛型版本IEnumerator<T>
class EnumeratorTest2 { private bool m_bBreak; public EnumeratorTest2(bool bBreak) { m_bBreak = bBreak; } public IEnumerator<string> GetEnumerator() { yield return "Hello"; if (m_bBreak) { yield break; } yield return "World"; } } EnumeratorTest2 eTest2 = new EnumeratorTest2(true); IEnumerator<string> e2 = eTest2.GetEnumerator(); while (e2.MoveNext()) { Console.WriteLine(e2.Current); }
编译后生成的代码如下:
class EnumeratorTest2 { private bool m_bBreak; public EnumeratorTest2(bool bBreak) { m_bBreak = bBreak; } public IEnumerator<string> GetEnumerator() { GenerateEnumerator2 e2 = new GenerateEnumerator2(0); e2.eTest2 = this; return e2; } private sealed class GenerateEnumerator2 : IEnumerator<string>, IEnumerator, IDisposable { // Fields private int state; private string current; public EnumeratorTest2 eTest2; // Methods public GenerateEnumerator2(int state) { this.state = state; } bool IEnumerator.MoveNext() { switch (this.state) { case 0: this.state = -1; this.current = "Hello"; this.state = 1; return true; case 1: this.state = -1; if (eTest2.m_bBreak) { break; } this.current = "World"; this.state = 2; return true; case 2: this.state = -1; break; } return false; } void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } // Properties string IEnumerator<string>.Current { get { return this.current; } } object IEnumerator.Current { get { return this.current; } } } } EnumeratorTest2 eTest2 = new EnumeratorTest2(true); IEnumerator<string> e2 = eTest2.GetEnumerator(); while (e2.MoveNext()) { Console.WriteLine(e2.Current); }
非泛型版本IEnumerable
class EnumeratorTest3 { public IEnumerable Test1() { yield return 100; yield return 200; } } EnumeratorTest3 eTest3 = new EnumeratorTest3(); foreach (object o in eTest3.Test1()) { Console.WriteLine(o); }
编译后生成的代码如下:
class EnumeratorTest3 { public IEnumerable Test1() { return new GenerateEnumerable3(0); } private sealed class GenerateEnumerable3 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable { // Fields private int state; private int current; // Methods public GenerateEnumerable3(int state) { this.state = state; } bool IEnumerator.MoveNext() { switch (this.state) { case 0: this.state = -1; this.current = 100; this.state = 1; return true; case 1: this.state = -1; this.current = 200; this.state = 2; return true; case 2: this.state = -1; break; } return false; } IEnumerator<object> IEnumerable<object>.GetEnumerator() { return this; } IEnumerator IEnumerable.GetEnumerator() { return this; } void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } // Properties object IEnumerator<object>.Current { get { return this.current; } } object IEnumerator.Current { get { return this.current; } } } } EnumeratorTest3 eTest3 = new EnumeratorTest3(); IEnumerator e3 = eTest3.Test1().GetEnumerator(); while (e3.MoveNext()) { Console.WriteLine(e3.Current); }
泛型版本IEnumerable<T>
class EnumeratorTest4 { private bool m_bBreak; public EnumeratorTest4(bool bBreak) { m_bBreak = bBreak; } public IEnumerable<float> Test1() { yield return 1.0f; if (m_bBreak) { yield break; } yield return 3.0f; } } EnumeratorTest4 eTest4 = new EnumeratorTest4(true); foreach (object o in eTest4.Test1()) { Console.WriteLine(o); }
编译后生成的代码如下:
class EnumeratorTest4 { private bool m_bBreak; public EnumeratorTest4(bool bBreak) { m_bBreak = bBreak; } public IEnumerable<float> Test1() { GenerateEnumerable4 e4 = new GenerateEnumerable4(0); e4.eTest4 = this; return e4; } private sealed class GenerateEnumerable4 : IEnumerable<float>, IEnumerable, IEnumerator<float>, IEnumerator, IDisposable { // Fields private int state; private float current; public EnumeratorTest4 eTest4; // Methods public GenerateEnumerable4(int state) { this.state = state; } bool IEnumerator.MoveNext() { switch (this.state) { case 0: this.state = -1; this.current = 1.0f; this.state = 1; return true; case 1: this.state = -1; if (this.eTest4.m_bBreak) { break; } this.current = 3.0f; this.state = 2; return true; case 2: this.state = -1; break; } return false; } IEnumerator<float> IEnumerable<float>.GetEnumerator() { return this; } IEnumerator IEnumerable.GetEnumerator() { return this; } void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } // Properties float IEnumerator<float>.Current { get { return this.current; } } object IEnumerator.Current { get { return this.current; } } } } EnumeratorTest4 eTest4 = new EnumeratorTest4(true); IEnumerator<float> e4 = eTest4.Test1().GetEnumerator(); while (e4.MoveNext()) { Console.WriteLine(e4.Current); }