C#のyield returnで出来ることは?簡単なサンプルや有効活用法までご紹介

- SE
- C#のyield returnはどのようなことが出来るのでしょうか。
- PM
- コレクションを順次返却できたり、記述が簡潔になります。
C#のyield returnとは?
C#のreturnについて知っている人は多いと思われますが、yield returnについては知らない人も多いのではないでしょうか。yield returnを活用すると複数のデータを扱う場合に効率的なプログラムを記述することができます。
yield returnとreturnの大きな違いは、returnは呼ばれると実行中のメソッドからすぐに抜けて呼び出し元に戻りますが、yield returnは戻らずに値のみを返すという点です。これからyield returnを実例で説明しますので、是非マスターしてください。
yield returnの簡単なサンプル
以下はyield returnのC#サンプルです。なおソースの先頭に「using System;」「using System.Collections.Generic;」を記述してください。1つ目はVisual Studioの場合は最初から入っています。
static IEnumerable<int> OneToTen()
{
for (int i = 1; i <= 10; i++)
{
yield return i;
}
}
static void Main(string[] args)
{
foreach (int num in OneToTen())
{
Console.Write(num + ” “);
}
}
yield returnでコレクションを順次返却できる
上のC#のサンプルを実行すると、以下のように表示されます。
1 2 3 4 5 6 7 8 9 10
yield returnはコレクション(IEnumerable)の形式で、メソッドから抜けずに値を順次、返すことができるのです。なおOneToTenクラスは以下のように記述しても全く同じ動作をします。
static IEnumerable<int> OneToTen()
{
var l = new List<int>();
for (int i = 1; i <= 10; i++)
{
l.Add(i);
}
return l;
}
yield returnを使えば記述が簡潔になる
2つ目のサンプルのOneToTenのように記述してもかまいませんが、yield returnを使用すれば返却するために一時的にListを生成する必要がありません。yield returnでスマートな記述が可能になります。
yield returnを使う場合、もう一つ理解しておくことがあります。以下のC#のサンプルを見てください。
static IEnumerable<int> OneToFive()
{
yield return 1;
yield return 2;
yield return 3;
//yield returnを中止したい
return;
yield return 4;
yield return 5;
}
static void Main(string[] args)
{
foreach (int num in OneToFive())
{
Console.Write(num + ” “);
}
}
yield returnの中断にはyield breakを使用する
上のようにyield returnを途中でやめたい場合はどうすればいいのでしょうか。上のようにreturn;を記述しても、IEnumerable<int>の値を返していないためエラーになります。return 0;のようにしても同様です。break;でもやはりエラーです。
こういった時は以下のようにyield breakを使用します。
static IEnumerable<int> OneToFive()
{
yield return 1;
yield return 2;
yield return 3;
//yield returnを中止したい
yield break;
yield return 4;
yield return 5;
}
ループ処理ではない時の中断はyield break
上のC#のサンプルを実行すると、以下のように表示されます。yield breakにより中断されています。
1 2 3
最初のOneToTenのようにforループでyield returnをしている場合、中断したい時はbreakで止められます。しかし今回のOneToFiveのようにループ処理ではない場合は、中断するためにyield breakが必要になります。yield returnとセットで覚えておきましょう。
yield returnの有効活用
ここまでのC#サンプルを見て、yield returnがあまり役に立つように感じないかもしれません。単純な処理であればList型のローカル変数にAddしていって返す、という方法でもいいでしょう。
しかしyield returnが有効なケースもあり、その一つが再帰処理です。以下のサンプルをご覧ください。なお実行する場合はソースの先頭に「using System.IO;」を追加してください。
static IEnumerable<string> GetFileNames(string folder)
{
string[] files = Directory.GetFiles(folder); // ファイル一覧を取得
foreach(string fi in files)
{
yield return fi; // ファイル名を返す
}
string[] folders = Directory.GetDirectories(folder); // フォルダ一覧を取得
foreach(string fo in folders)
{
foreach (string fi in GetFileNames(fo)) // 自分自身をコール
{
yield return fi; // フォルダ内のファイル名を返す
}
}
}
static void Main(string[] args)
{
foreach (string str in GetFileNames(“c:\\test”))
{
Console.WriteLine(str);
}
}
再帰処理でyield returnを利用する
上のC#サンプルのGetFileNamesメソッドは、ツリー構造になっているフォルダ内のファイルの一覧を全て表示します。呼ばれると、まずDirectory.GetFilesで現在の階層の全てのファイル一覧をyield returnで返します。
次にDirectory.GetDirectoriesで現在の階層の全てのフォルダ一覧を取得し、各フォルダをパラメータとして、自分自身であるGetFileNamesを呼び出します。このような作り方を再帰処理と言います。
再帰処理ではyield returnを有効活用できる
自分自身のGetFileNamesを呼び出す再帰処理では、下の階層のファイル一覧を取得できます。それをyield returnで呼び出し元に返します。まるでバケツリレーのように、下の階層の情報が上に向かってハンドリングされていくのです。
そして最初にGetFileNamesを呼び出したMainメソッドに、各階層のすべてのファイル一覧が届きます。ここでは一括でそれを表示できるのです。実行結果は以下のようになります(結果はtestフォルダ内の状況によります)。
c:\test\a.txt
c:\test\b.png
c:\test\test2\c.txt
c:\test\test2\test3\d.txt
yield returnで再帰処理が綺麗な構造になる
上のような再帰処理でyield returnを使わず、別に用意したListに次々にAddして、最後にそれを表示するというやり方も、もちろん可能です。ただそれよりもこのサンプルのように内部だけで完結している方が、汎用性の高いプログラムと言えます。
yield returnが無い他の言語からC#を始める人には、yield returnを使用しない方がわかりやすいのは確かですが、C#の機能を活用してプログラムする方がベターです。
- SE
- 再帰処理でyield returnが有効活用できるのですね。
- PM
- yield returnを使うと内部だけで完結するので、汎用性の高いプログラムと言えるでしょう。
yield returnを活用してC#らしさを発揮しよう
yield returnについて解説しましたがいかがでしたでしょうか。yield returnは他のやり方でも代用できる機能ではありますが、上手く利用すればC#らしいプログラムを記述することができます。yield returnを積極的に活用しましょう。