(1) C#のIEnumerableとは?使い方(foreachでの要素取り出し)を順を追って解説
(1-1) IEnumerableとは?
(1-2) IEnumerableの実装の流れ
(1-2-1) STEP1:IEnumerableインターフェイスの実装
(1-2-2) STEP2:IEnumeratorインターフェイスの実装
(1-3) サンプルプログラムで理解を深める
(1) C#のIEnumerableとは?使い方(foreachでの要素取り出し)を順を追って解説
(1-1) IEnumerableとは?
C#のインターフェイスでカスタム配列への対話的なアクセス(foreachによるアクセス)を可能にします。
■IEnumerableの概要
IEnumerableインターフェイスは抽象メソッドGetEnumerator()メソッドのみを持つインターフェイスで、戻り値は「IEnumerator」インターフェイスを返却します。この「IEnumerator」インターフェイスを使うとカスタム配列の要素に対して、繰り返し処理でアクセスできる様になります。
(図111)
■IEnumeratorで出来る事・出来ない事
IEnumeratorインターフェイスは3つメソッドMoveNext()とReset()とCurrentがあります。「IEnumerable」を実装すると、foreachでループして要素を取得できるようになります。注意点として「IEnumerator」インターフェイスは要素に対してアクセスはできますが、変更する事はできません。
■IEnumeratorで配列にアクセスする際のお作法
通常、配列の要素にアクセスする際は「インデックス」を使いまが、IEnumeratorではインデックスの代わりに3つの抽象メソッド「Reset()」と「MoveNext()」と「Current()」を使って要素にアクセスします。
(1-2) IEnumerableの実装の流れ
ここからはIEnumerableの実装の流れ
(1-2-1) STEP1:IEnumerableインターフェイスの実装
まずは最初に「IEnumerable」インターフェイスを実装します。このインターフェイスには「GetEnumerator()」メソッドがあり、我々のカスタムクラスのアクセスに使う「IEnumerator」のオブジェクトを返却します。
(例)GetEnumeratorメソッドの実装
public IEnumerator GetEnumerator() { return (IEnumerator)this; }
(1-2-2) STEP2:IEnumeratorインターフェイスの実装
次に「IEnumerator」インターフェイスを実装します。このインターフェイスは下記3つのメソッドがあり、これらの3つのメソッドを実装するとforeachによるループが可能となります。
// IEnumeratorインターフェイスのメソッドシグニチャ void Reset() bool MoveNext() object Current
①void Reset()メソッド
まずはReset()メソッドの実装です。Reset()メソッドはポインタ(現在の要素を指す変数)を開始地点にセットします(最初の要素の前に0番目の空要素があるイメージ)。ですので、インデックスとしては「-1」を割り当てています。
(例)Reset()メソッドの実装
public void Reset() { [現在位置変数名] = -1; }
②bool MoveNext()メソッド
MoveNext()は次の要素に移動するためのメソッドです。内部的には現在位置(カレントポジション)の次の要素があるならばtrueを返却し、次の要素が無い場合はfalseを返却します。
(例)MoveNext()メソッドの実装
public bool MoveNext() { if ([現在位置変数名] < [配列名].Count - 1) { ++[現在位置変数名]; return true; } return false; }
③object Current()メソッド
特定の位置にある要素を返却するために使います。実際に要素を順番に取り出していく際は「MoveNext ()」と「Current」を組み合わせて使っていく形になります。
(例)Current()メソッドの実装
public object Current { get { return PersonList[CurrPosition]; } }
(1-3) サンプルプログラムで理解を深める
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IEnumeratorTest { //①個人を管理するPersonクラス class Person { private string FirstName; private string LastName; private int Age; public Person(string firstName, string lastName, int age) { this.FirstName = firstName; this.LastName = lastName; this.Age = age; } public override string ToString() { return "Name:" + FirstName + " " + LastName + " Age: " + Age.ToString(); } } //②複数人の集団を管理するPeopleクラス class People { ArrayList PersonList = new ArrayList(); public void AddPerson(Person oPeople) { PersonList.Add(oPeople); } } class Program { static void Main(string[] args) { //Peopleクラス(複数人の集団)のインスタンス作成 People plist = new People(); //Personクラス(個人)のインスタンス作成 Person p1 = new Person("Suzuki", "Hanako", 29); Person p2 = new Person("Yamada", "Taro", 19); //People(集団)にPerson(個人)を追加 plist.AddPerson(p1); plist.AddPerson(p2); Console.WriteLine("### foreachを用いてPersonsを数え上げ開始 ###"); foreach (Person person in plist) { Console.WriteLine(person); } } } }
エラー CS1579 foreach ステートメントは、'People' が 'GetEnumerator' のパブリック インスタンス定義を含んでいないため、型 'People' の変数に対して使用できません
(図131)エラー
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IEnumeratorTest2 { //①個人を管理するPersonクラス class Person { private string FirstName; private string LastName; private int Age; public Person(string firstName, string lastName, int age) { this.FirstName = firstName; this.LastName = lastName; this.Age = age; } public override string ToString() { return "Name:" + FirstName + " " + LastName + " Age: " + Age.ToString(); } } //②複数人の集団を管理するPeopleクラス class People : IEnumerable, IEnumerator { ArrayList PersonList = new ArrayList(); public void AddPerson(Person oPeople) { PersonList.Add(oPeople); } // (1) IEnumerableの実装(★追記) public IEnumerator GetEnumerator() { return (IEnumerator)this; } // (2) IEnumeratorの実装(★追記) // 現在位置(カレントポジション)の変数定義 private int CurrPosition = -1; // (2-1) void Reset()メソッド(★追記) public bool MoveNext() { if (CurrPosition < PersonList.Count - 1) { ++CurrPosition; return true; } return false; } // (2-2) bool MoveNext()メソッドの実装(★追記) public void Reset() { CurrPosition = -1; } // (2-3) object Current()メソッドの実装(★追記) public object Current { get { return PersonList[CurrPosition]; } } } class Program { static void Main(string[] args) { //Peopleクラス(複数人の集団)のインスタンス作成 People plist = new People(); //Personクラス(個人)のインスタンス作成 Person p1 = new Person("Suzuki", "Hanako", 29); Person p2 = new Person("Yamada", "Taro", 19); //People(集団)にPerson(個人)を追加 plist.AddPerson(p1); plist.AddPerson(p2); Console.WriteLine("### foreachを用いてPersonsを数え上げ開始 ###"); foreach (Person person in plist) { Console.WriteLine(person); } } } }
(図132)