C#でのイテレータパターン
1. Iteratorパターン 1 | TECHSCORE(テックスコア)
これをC#で書く。イテレータはC#だとIEnumerator<T>
なので、イテレータパターンはIEnumerable<T>
を実装するパターンということになる。
public class MyStudentList : StudentList, IEnumerable<Student> { public MyStudentList() { } public MyStudentList(int studentCount): base(studentCount) { } public IEnumerator<Student> GetEnumerator() { return new MyStudentListIterator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public class MyStudentListEnumarator : IEnumerator<Student> { private MyStudentList myStudentList; private int index; private int startIndex; public MyStudentListEnumarator(MyStudentList list) { this.myStudentList = list; this.index = -1; this.startIndex = 0; } public Student Current { get { return myStudentList.GetStudentAt(index); } } object IEnumerator.Current { get { return Current; } } public bool MoveNext() { if (myStudentList.GetLastNum() > ++index) { return true; } index = myStudentList.GetLastNum(); return false; } public void Reset() { index = startIndex - 1; } public void Dispose() { } }
サンプルの通りに書くとこんな感じになる。
要は、IEnumerable<T>
を実装していないStudentList
はクソなので、継承してIEnumerable<T>
を追加実装しちゃいましょう、と。
ここでIEnumerator<T>
ってどうやって実装するんだろうとReference Source見てみたんだけど、まちまちで面白い。MoveNext()
⇒Current
の順で呼んだときにうまく動けばよいみたいで、List<T>.Enumerator
では
MoveNext()
で今のインデックスの値を取っておいてからインデックスを進めるCurrent
で取得済みの値を返す
という処理になっている一方、ArrayEnumerator
では
MoveNext()
でインデックスを進めるCurrent
で今のインデックスの値を返す
となっている。
私は後者の方が直感的だと思ったので、上記ではそのように実装。
ところで、IEnumerator<T>
を実装してみるのは勉強になって面白いが、C#にはyield return
という便利なものがあるので通常はこんな面倒な実装はしなくて良くて、以下の実装で十分。
public class MyStudentList : StudentList, IEnumerable<Student> { public MyStudentList() { } public MyStudentList(int studentCount): base(studentCount) { } public IEnumerator<Student> GetEnumerator() { var lastNumber = GetLastNum(); for (var i = 0; i < lastNumber; i++) { yield return GetStudentAt(i); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }