がりらぼ

WindowsRuntimeの応援ブログ

C#の主要インターフェース解説:IEnumerable

IEnumerableとは

「アイ エニュメラブル」と読むそうです。

このインターフェースを実装することによって実現できる機能は、「foreachでコレクション処理できる」ことです。

foreachといえば

foreach(int i in collection)
{
    Console.WriteLine(i);

}

みたいな感じでコレクションを列挙することができるアレです。

実はforeachはIEnumerableインターフェースを実装しているクラスしか処理することができません

配列やListなどはすでにIEnumerableインターフェースを実装しているので「C#の配列とかはforeachで処理できるものだ」と認識しちゃいますがIEnumerableインターフェースがあってこそのforeachです。

IEnumerator

では実際にIEnumerableを実装したMyCollectionクラスを見ていきます。

public class MyCollection:IEnumerable
{
    int[] hoge;
    public MyCollection()
    {
        hoge=new int[]{0,1,2,3};
    }

    public IEnumerator GetEnumerator()
    {
        for (int i = 0; i < hoge.Length; i++) 
        {
            yield return hoge[i];
        }

    }
}

IEnumerableで実装が保証されているメソッドGetEnumeratorメソッドです。

このメソッドIEnumeratorインターフェースを実装しているクラスのインスタンスを返すメソッドです。

まとめると、IEnumerableインターフェースはGetEnumeratorメソッドでIEnumeratorインターフェースを実装するクラスのインスタンスを返すことを保証するインターフェースであるとも言えます。

ではIEnumeratorインターフェースを見てみましょう。

f:id:garicchi:20140911190301p:plain

Currentはコレクションの現在指している要素を参照します。 MoveNextメソッドはCurrentプロパティをコレクションの次の要素に変えます。もし次の要素がない場合はfalseを返します。 Resetメソッドはコレクションを指す位置を初期位置に戻します。

つまりIEnumeratorインターフェースは「コレクションの現在の要素を取得したり、次の要素に進めたり、最初の要素にもどったりする機能を保証しています。

IEnumerableインターフェースはこのコレクションの要素を参照したり次に移したりなどをするIEnumeratorインターフェースを実装するクラスのインスタンスを返すことを保証します。

でも実際、IEnumeratorインターフェースを実装するクラスを作るのはめんどうなので、そのためにイテレータというものがC#にはあります。

イテレータ(yield)

イテレータとは、yield returnで要素が返されるたびに、コレクションの次の要素をforeachで処理できるような形で返すことができるものです。

実際にGetEnumuratorメソッドをyield returnで実装してみると、まるでIEnumeratorインターフェースを実装したクラスを返しているような見た目でGetEnumeratorメソッドを実装することができます。

f:id:garicchi:20140911191450p:plain

ごちゃごちゃ書きましたが、大雑把に書くと、「yield returnをするとIEnumeratorを返すことができる」と考えます。

foreachで列挙してみる

yield returnを使って簡単にIEnumerableインターフェースをMyCollectionに実装することができたのでforeachで列挙してみましょう。

MyCollection collection = new MyCollection();
foreach (int i in collection)
{
    Console.WriteLine(i);
}

これでコレクションを列挙することができます。

foreachはコンパイル時にこんな感じになります。

IEnumerator enumerator=collection.GetEnumerator();
while (enumerator.MoveNext())
{
    object obj = enumerator.Current;
    Console.WriteLine(obj.ToString());
}

IEnumeratorインターフェースにはMoveNextで次の要素、Currentで現在の要素を返すと先ほど書きましたが、理にかなった処理が行われることがわかります。

MyCollectionは配列をforeachで処理できるように実装しましたが、もともと配列はIEnumerableを実装するのでなんか意味あるの?って感じですが、IEnumerableは「自作のコレクションクラスを作ることができます。

たとえばMyCollectionのGetEnumeratorメソッドをこんな感じにすると、偶数要素だけ返すコレクションを作れちゃったりします。

public IEnumerator GetEnumerator()
{
    for (int i = 0; i < hoge.Length; i++) 
    {
        if(i%2==0)
        yield return hoge[i];
    }

}

また、yield returnは呼ばれるたびにforeachで処理される要素をひとつ返すので何度も呼ぶことでまるで連続した値があるように実装できます。

public IEnumerator GetEnumerator()
{
    yield return 9;
    yield return 1;
    yield return 3;
    yield return 2;
    yield return 4;
    yield return 5;
    yield return 6;

}

まとめ

  • IEnumerableインターフェースは実装することによってforeachで処理を行うことが可能になる。
  • IEnumerableのGetEnumeratorでyield returnをすることによってforeachで処理する値をひとつかえすことができる。
  • IEnumerableで自作コレクションをつくることができる