LoginSignup
6
1

More than 5 years have passed since last update.

テキストストリームを一行ずつ切り分けたIEnumerable<string>にする

Last updated at Posted at 2017-09-16

ネタ元: 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は便利ですね。
処理の流れが読み取りにくくなるので複雑な処理で使うとドはまりしそうだけど。

6
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1