PR

[C#] イテレーターを使ってforeachで使えるメソッドを作る(yield)

IEnumerable<T>を返すメソッドでyieldを使うとforeachのinのところで使えるようになるよ、という話。

foreachを使ってオブジェクトから要素を取り出して処理するには、IEnumerableインターフェースを使い要素を返すメソッドを実装する必要があります。

IEnumberableインターフェースを実装するのは大変なので、C#はイテレーターという機能を用意しています。イテレーターを使うことでforeachのinに指定できる反復可能なオブジェクトを返すメソッド、プロパティを実装することができます。

スポンサーリンク

イテレーター(Iterator)の使い方

イテレーターとしてメソッド、プロパティを使うには、値を返すときにブロック { } 内で yield retun を使います。

using System;
using System.Collections.Generic;
class Program
{
    public static void Main()
    {
        var cls = new Sample();
        // foreachにイテレーターを指定
        foreach(var i in cls.IteratorMethod()) Console.WriteLine(i);

        // foreachにイテレーターを指定
        foreach (var i in cls.IteratorProperty) Console.WriteLine(i);
    }
}

class Sample {
    // ↓これがイテレーター(メソッド)
    public IEnumerable<int> IteratorMethod() {
        for (var i = 0; i < 10; i++) {
            // yield returnに指定するのは <T> で指定した型の値
            yield return i;
        }
    }

    // ↓これもイテレーター(プロパティ)
    public IEnumerable<string> IteratorProperty {
        get {
            for (var i = 0; i < 10; i++)
            {
                // yield returnに指定するのは <T> で指定した型の値
                yield return i.ToString();
            }
        }
    }
}

戻り値にはIEnumberable<T>を指定します。
yield retun にはIEnumberable<T>の<T>の型の値を指定します。

スポンサーリンク

yield retun について詳しく

yield return はこんな書き方もできます。

using System;
using System.Collections.Generic;
class Program
{
    public static void Main()
    {
        foreach (var a in iterator(10)) Console.WriteLine(a);
    }

    static IEnumerable<int> iterator(int i) {
        // yield retun を並べて書くこともできる
        yield return i + 1;
        yield return i + 2;
        yield return i + 3;
        yield return i + 4;
        yield return i + 5;
    }
}

処理結果はこんな感じです。

11
12
13
14
15

yield return は並べて書いて複数の要素を返すことができます。
yield return まで処理が来ると一旦指定された値を呼び出し元に返します。
そして呼び出し元の処理が終わるとまたイテレーターに戻ってきます。

たしかめてみましょう、

using System;
using System.Collections.Generic;
class Program
{
    public static void Main()
    {
        foreach (var a in iterator(10)) Console.WriteLine(a);
    }

    static IEnumerable<int> iterator(int i) {
        Console.WriteLine("★");
        yield return i + 1;
        Console.WriteLine("★★");
        yield return i + 2;
        Console.WriteLine("★★★");
        yield return i + 3;
        Console.WriteLine("★★★★");
    }
}

これを実行するとこんな感じになります。

★
11
★★
12
★★★
13
★★★★

こんな流れでプログラムが動きます。
12行目のyield return まで処理が来たら呼び出し元(7行目)に値を返す
7行目でyield returnで返ってきた値をコンソールに出力(11が出力される)
13行目に処理が戻ってきて、★★をコンソールに出力
14行目のyield return まで処理が来たら呼び出し元(7行目)に値を返す
7行目でyield returnで返ってきた値をコンソールに出力(12が出力される)
というふうにyeild return 毎に値を呼び出し元に返しています。

これの何がいいの?というと、
大量のデータをひとまとめにして配列やコレクションとして返そうとした場合、データをすべて読み込んでから配列などを作りそれを返すということ必要で、データの読み込みのあいだ処理が止まったように見えます。
yield return を使うとデータが見つかるとすぐに呼び出し元に値を返し、また戻ってきてすぐ次のデータを返すということができるので、処理が止まらずに済みます。


C# 記事まとめページに戻る(他のサンプルコードもこちら)

C# プログラミング講座
C#についての記事まとめページです。開発環境VisualStudioのインストール方法や使い方、プログラミングの基礎知識についてや用語説明の記事一覧になっています。講座の記事にはすぐに実行できるようにサンプルコードを載せています。

コメント