Advent LINQ (3): FilesAsEnumerable

Directory.GetFilesが配列を返すので、列挙子にしてみる。特にSearchOption.AllDirectoriesが指定されると、処理が返って来るまでにかなり時間がかかることがある。
フォルダーは再帰探索する必要があるので、実装は少し捻る必要がある。

public static class LinqExtensions
{
    public static IEnumerable<string> FilesAsEnumerable(string path, string patterns)
    {
        foreach (var directoryPath in
            Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly))
        {
            foreach (var filePath in
                FilesAsEnumerable(directoryPath, patterns))
            {
                yield return filePath;
            }
        }

        foreach (var filePath in
            Directory.GetFiles(path, patterns, SearchOption.TopDirectoryOnly))
        {
            yield return filePath;
        }
    }
}

これでもいいのだが、foreachだらけで見通しが悪い。全てクエリに置き換える。

public static class LinqExtensions
{
    public static IEnumerable<string> FilesAsEnumerable(string path, string patterns)
    {
        var byDirectory =
            from directoryPath in Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly)
            from filePath in FilesAsEnumerable(directoryPath, patterns)
            select filePath;

        var byFile =
            from filePath in Directory.GetFiles(path, patterns, SearchOption.TopDirectoryOnly)
            select filePath;

        return byDirectory.Concat(byFile);
    }
}

これでファイルの列挙は以下のように記述できる。

foreach (var path in LinqExtensions.FilesAsEnumerable(@"C:project", "*.cs"))
{
    Console.WriteLine(path);
}

ファイルの逐次探索が可能になったので、大量のファイルをパイプライン処理できるようになった。
なお、このコードは.NET 4.0以降では、Directory.EnumerateFilesとほぼ同じ動作となる。