Iterator pattern is one of design patterns, because of its universality, many languages have built-in native support.
In. NET, the iterator pattern is passed through IEnumerator,IEnumerable Two interfaces (non-generic and generic versions) are encapsulated
An important aspect of the iterator pattern is that instead of returning all data at once, only one element is returned per call.
The relationships among Array, IEnumerable and IEnumerator are as follows:
foreach traversal
(1) Array and collection container classes are derived from the IEnumerable interface class (which contains an IEnumerator GetEnumerator() interface function). Array and collection container classes implement this function, which allows foreach to traverse it.
Note: You don't need to derive from IEnumerable, as long as the class implements the IEnumerator GetEnumerator() function, you can use foreach to traverse the elements in that class.
(2) IEnumerator defines the Current attribute to return the element where the cursor is located, MoveNext moves to the next element (true if there are elements, false if at the end), and Reset resets the cursor to the first position.
int[] IntArray = new int[] { 1, 2, 3 }; foreach (int n in IntArray) { Console.WriteLine(n); } // Above foreach Equivalent to the following code implementation IEnumerator e = IntArray.GetEnumerator(); try { while (e.MoveNext()) { Console.WriteLine((int)e.Current); } } finally { var disposable = e as IDisposable; if (disposable != null) { disposable.Dispose(); } }
In C 1.0, creating an IEnumerator enumerator requires a lot of code. C#2.0 adds the yield statement to allow the compiler to automatically create the IEnumerator enumerator.
The yield return statement returns an element of the collection and moves to the next element. yield break stops iteration.
Non-generic version IEnumerator
class EnumeratorTest { public IEnumerator GetEnumerator() { yield return 100; yield return "Good"; } } EnumeratorTest eTest = new EnumeratorTest(); foreach (object o in eTest) { Console.WriteLine(o); }
The code generated after compilation is as follows:
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(); } }
Generic version 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); }
The code generated after compilation is as follows:
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); }
Non-generic version 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); }
The code generated after compilation is as follows:
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); }
Generic version 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); }
The code generated after compilation is as follows:
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); }