ネタ元: C#:IEnumerableの実装サンプルとして簡易catコマンドを作成してみた
ええーっ、IEnumeratorの実装クラス作らなくてもGetEnumerator()の中でyield returnするだけでいいのかー。すごーい!便利!
だとしたら拡張メソッドにできるんじゃね?そうしたらラッパークラスと違って新たなインスタンスを作らなくて済むから効率的じゃない?
てことでやってみました。
面白そうだから作ってみただけだけど「テキストストリームをLinqで処理できる」というのは意外と使える場面があるかもしれない。
public static class LineEnumerableExtentions
{
//TextReaderから一行ずつを要素にして返すよ
public static IEnumerable<string> AsLineEnumerable(this TextReader source)
{
string s;
while ((s = source.ReadLine()) != null)
{
yield return s;
}
}
}
//実行サンプル(C#6.0以降)
class Program
{
static void Main(string[] args)
{
Console.In.AsLineEnumerable().
All(line => { Console.WriteLine($"[{line}]"); return true; });
}
}
実行サンプルについての補足
Linq内部で実行させる処理は値を返さないといけない。なので{voidを返す処理; return true;}
みたいにするわけだけど、それをSelect()に処理させるとIEnumeableを返すことになる。
All()だとboolを返すだけなのでちょっぴり効率がいいはず。
そのかわり必ずtrueを返さないとfalseを返したところで実行が中断されるので注意。
Streamの拡張メソッドがない理由
「ネタ元の方はStreamもソースにできるじゃない!」と思った人へ。
元ネタではStreamを受け取ってEnumerableTextReaderクラス内部でStreamReaderを作ってますよね。
普通に考えると拡張メソッドのローカル変数としてStreamReaderを作れば同じことができそうなんだけど、StreamReaderはClose()やDispose()のときに元のストリームもCloseするんです。
そのため拡張メソッド化した場合、内部で作ったStreamReaderをメソッド終了時にDisposeしないのは行儀が悪い。でもDisposeすると元のストリームまでCloseしてしまう、つまり「メソッドを実行するだけでそのストリームはクローズしてしまう」という厄介な挙動になってしまいます。
StreamReaderを作らずに直接Streamから読み出せば実装不可能ではないんだけど、さすがにめんどくさすぎるのでやらない。
StreamReaderを作るところまでは自分でやってください。その方がいろいろ柔軟性も高いだろうし。
それにしてもyield return
は便利ですね。
処理の流れが読み取りにくくなるので複雑な処理で使うとドはまりしそうだけど。