hakeの日記

Windows環境でプログラミングの勉強をしています。

C# - foreach文とIEnumerableインターフェース

foreach文において in の後に置けるオブジェクトはIEnumerableインターフェースを継承したクラスとなる。
IEnumerableは、GetEnumeratorメソッドでIEnumeratorオブジェクトを返す。

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

GetEnumeratorメソッドで返されたIEnumerableオブジェぅとは以下のプロパティとメソッドを持つ

interface IEnumerator
{
    object Current { get; }
    void Reset();
    bool MoveNext();
}

foreach文において、GetEnumeratorメソッドでIEnumeratorを取得し、そのMoveNextメソッドがtrueを返す間は、Currentプロパティを要素を取得してブロック内の処理を行う。
Currentプロパティに要素の取得前には必ずMoveNextメソッドが呼ばれるので、初期状態においては要素を指す内部インデックスは最初の要素の前を指している必要はある。
 

using System;
using System.Collections;

//IEnumerableとIEnumerator - 非ジェネリックコレクション版

//IEnumerable は、IEnumerator GetEnumerator()メソッドを持つ
//IEnumerator は、以下を持つ
//  object Currentプロパティ(get)
//  void Reset()メソッド
//  bool MoveNext()メソッド

class MyIntEnumerator : IEnumerator, IEnumerable
{
    private int[] data;
    private int index = -1;

    public MyIntEnumerator(int[] arg){ this.data = arg; }
    
    public object Current
    {
        get
        {
            Console.WriteLine("Current値( {0} )が読みだされました", this.data[this.index]);
            return this.data[this.index];
        }
    }

    public void Reset()
    {
        Console.WriteLine("Reset()が実行されました");
        this.index = -1;
    }

    public bool MoveNext()
    {
        Console.WriteLine("MeveNext()が実行されました");
        this.index++;
        if(this.index >= this.data.Length)
        {
            Console.WriteLine("戻り値はFalseです");
            return false;
        }
        else
        {
            Console.WriteLine("戻り値はTrueです");
            return true;
        }
    }

    public IEnumerator GetEnumerator()
    {
        Console.WriteLine("\nGetEnumerator()が実行されました");
        this.Reset();
        return this;
    }
}


class MyMain
{
    public static void Main(string[] args)
    {
        MyIntEnumerator me = new MyIntEnumerator(new int[]{10,20,30});
        foreach(var e in me)
        {
            Console.WriteLine("foreach内: {0}", e );
        }


        foreach(var e in me)
        {
            if((int)e == 20) break;
            Console.WriteLine("foreach内: {0}", e );
        }
    }
}

 

実行結果

GetEnumerator()が実行されました
Reset()が実行されました
MeveNext()が実行されました
戻り値はTrueです
Current値( 10 )が読みだされました
foreach内: 10
MeveNext()が実行されました
戻り値はTrueです
Current値( 20 )が読みだされました
foreach内: 20
MeveNext()が実行されました
戻り値はTrueです
Current値( 30 )が読みだされました
foreach内: 30
MeveNext()が実行されました
戻り値はFalseです

GetEnumerator()が実行されました
Reset()が実行されました
MeveNext()が実行されました
戻り値はTrueです
Current値( 10 )が読みだされました
foreach内: 10
MeveNext()が実行されました
戻り値はTrueです
Current値( 20 )が読みだされました