hakeの日記

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

C# - foreach文とyield

IEnumerable<T>型のメソッドを定義して、その処理中に yield return 値; を記述することで、IEnumerable<T>オブジェクトを生成することができる。

    IEnumerable<int> MyEnumerable()
    {
        処理;
        yield return 値;
        処理;
    }

この方法を利用したほうが、IEnumerable<T>型やIEnumerator<T>型のクラスを定義するよりも簡単にEnumeratorが作成できる。
試しにforech文を使用せずに、GetEnumerator()メソッドでIEnumerator<T>型オブジェクトを取得したところ、GetNext()メソッドおよびCurrentプロパティで値を順番に取得することができた。

yield returnを使用した場合の動作は、IEnumerable<T>型のメソッド中でyield return文に出くわすと、foreach側に制御が移ってforeachブロックの処理を実行する。その処理が終了すると再びIEnumerable<T>型のメソッドに制御が移りyield return文の次から処理が行われるという形になる。これがIEnumerable<T>型のメソッドの処理が終了するまで継続する。
 

using System;
using System.Collections.Generic;

//yield

class OtherClass
{
    public IEnumerable<int> OtherIEnumerable()
    {
        for(var i = 10; i < 15; i++)
        {
            yield return i;
        }
    }
}


class MyMain
{
    public static void Main(string[] args)
    {
        //自クラスのstaticメソッドを使用
        foreach(var e in MyEnumerable())
        {
            Console.WriteLine("Main foreach内:値 {0} ", e);
        }
        Console.WriteLine("-----");

        //foreachを使用せずに読み出す
        var menumerable = MyEnumerable();
        var me = menumerable.GetEnumerator();
        bool b;
        b = me.MoveNext();
        Console.WriteLine( "MoveNext()実行: {0}", b );
        if( b ) Console.WriteLine( "Current取得   : {0}", me.Current );
        b = me.MoveNext();
        Console.WriteLine( "MoveNext()実行: {0}", b );
        if( b ) Console.WriteLine( "Current取得   : {0}", me.Current );
        Console.WriteLine("-----");

        //他クラスのインスタンスメソッドを使用
        var oe = new OtherClass();
        foreach(var e in oe.OtherIEnumerable())
        {
            Console.Write("{0} ", e);
        }
        Console.WriteLine();
        Console.WriteLine("-----");

        //自クラスのstaticメソッド(非ループ)を使用
        foreach(var e in MyEnumerable2())
        {
            Console.Write("{0} ", e);
        }
        Console.WriteLine();
        Console.WriteLine("-----");



    }

    //ループ処理
    public static IEnumerable<int> MyEnumerable()
    {
        for(var i = 0; i < 5; i++)
        {
            Console.WriteLine("MyEnumerable内:yield前:値 {0}", i);
            yield return i;
            Console.WriteLine("MyEnumerable内:yield後:値 {0}", i);
        }
    }

    //非ループ(yieldが複数個存在)
    public static IEnumerable<int> MyEnumerable2()
    {
        yield return 100;
        yield return 200;
        yield return 300;
    }
}

 

実行結果

MyEnumerable内:yield前:値 0
Main foreach内:値 0
MyEnumerable内:yield後:値 0
MyEnumerable内:yield前:値 1
Main foreach内:値 1
MyEnumerable内:yield後:値 1
MyEnumerable内:yield前:値 2
Main foreach内:値 2
MyEnumerable内:yield後:値 2
MyEnumerable内:yield前:値 3
Main foreach内:値 3
MyEnumerable内:yield後:値 3
MyEnumerable内:yield前:値 4
Main foreach内:値 4
MyEnumerable内:yield後:値 4
-----
MyEnumerable内:yield前:値 0
MoveNext()実行: True
Current取得   : 0
MyEnumerable内:yield後:値 0
MyEnumerable内:yield前:値 1
MoveNext()実行: True
Current取得   : 1
-----
10 11 12 13 14
-----
100 200 300
-----