@ledsun blog

無味の味は佳境に入らざればすなわち知れず

GetEnumeratorの実装。

C#で自作のクラスをforeach文で呼び出したい場合はGetEnumuratorメソッドを実装すればforeach文で呼び出せるようになります*1

次のforeach文は

foreach (int i in new Simplest())
{
    Console.WriteLine(i);
}

以下のシンタックスシュガーです。

IEnumerator e = new Simplest().GetEnumerator();
while (e.MoveNext()) {
    Console.WriteLine(i);
}

Java5で言うIteratorと拡張for文の関係と同じです。

実装方法

どうすればGetEnumuratorメソッドを実装できるでしょうか?
C#2.0以降では反復子ブロックという機能があり、比較的簡単な手順で実装することが出来ます。次にサンプルを示します。

using System;
using System.Collections.Generic;
using System.Data;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;

namespace EnumrableSample
{
    public class Simplest
    {
        public IEnumerator<int> GetEnumerator()
        {
            yield return 0;
        }

        [TestFixture]
        public class Test
        {
            [Test]
            public void ゼロがひとつだけ帰ってくる()
            {
                foreach (int i in new Simplest())
                {
                    Console.WriteLine(i);
                }
            }
        }
    }
}

重要なのは「yield return」です。yield return で指定した値を元にコンパイラが GetEnumerator メソッドの実処理を実装します。この例では一つの値を返します。

次のように複数指定すれば、指定した分の値を列挙するGetEnumeratorメソッドが実装されます。

public IEnumerator<int> GetEnumerator()
{
    yield return 0;
    yield return 1;
}

yield returnを含む「メソッドのようなもの」を、反復子ブロックといいます。次回はこの反復子ブロックがどのように実際のGetEnumeratorメソッドとして実装されるか、反復子ブロックを使う上での注意点などを書きたいと思います。*2

*1:c#では言語全体としてはduck typingをサポートしていませんが、foreach文の場合はサポートしてます。IEnumerableインタフェースを実装する必要はありません。

*2:http://www.atmarkit.co.jp/fdotnet/csharp20/csharp20_03/csharp20_03_02.htmlに書いてあるような話です。