3

これは、質問の目的を分離するための簡略化された例です。私の実際のシナリオでは、GetColumnReaderによって返されるColumnReaderは、実際には単なるReadLineよりも多くの作業を実行します。

次のプログラムを実行すると、Reader()を呼び出そうとするとエラーが発生します。もちろん、TextReaderはusingステートメントによって既に破棄されているためです。

public class Play{
    delegate string ColumnReader();
    static ColumnReader GetColumnReader(string filename){
        using (TextReader reader = new StreamReader(filename)){
            var headers = reader.ReadLine();
            return () => reader.ReadLine();
        }
    }
    public static void Main(string[] args){
        var Reader = GetColumnReader("Input.tsv");
        Console.WriteLine(Reader());
    }


}

または、「using」を削除してTextReaderを直接宣言することもできますが、これは機能しますが、TextReaderが最終的に閉じられるという保証はありません。

返されたラムダ関数に「デストラクタ」を追加する方法はありますか?ラムダ関数がスコープから外れると(参照がなくなると)すぐにTextReaderを破棄できる可能性がありますか?

他の提案も歓迎しますが、基本的なクロージャー構造を維持したいと思います(つまり、質問の範囲に収まります)。

4

6 に答える 6

3

ラムダ式を必要としない場合は、代わりに列挙可能を作成できます。

=> {}内を使用して移動する可能性は、実際のコードで機能する可能性があります...それでもおそらくあなたが探しているものではありません:

static ColumnReader GetColumnReader(string filename) {
   return () => {
     using (TextReader reader = new StreamReader(filename)) {
        var headers = reader.ReadLine();
        return reader.ReadLine();
     }
  };
}

IEnumerableを使用したバージョン(常に反復を終了する場合):

 static IEnumerable<string> GetColumnReader(string filename) {
   using (TextReader reader = new StreamReader("aa")) {
     var headers = reader.ReadLine();
     yield return reader.ReadLine();
   }
 }

列挙の途中までの反復をサポートする場合は、カスタムIDisposableイテレータを作成する必要があります。foreachこのような場合を処理するためにIDisposableを実装するイテレータをどのように処理するかをご覧ください。

于 2012-07-20T00:16:10.090 に答える
2

基本的に、デリゲート自体の外側にある使い捨て要素のスコープが必要です。このような状況では、デリゲートにファイル名ではなく使い捨てインスタンス(つまり、TextReader)を受け入れさせます。

于 2012-07-20T00:17:21.757 に答える
0
public class Play {

    delegate string ColumnReader();

    static ColumnReader GetColumnReader(string filename) {
        return () => {
            using (TextReader reader = new StreamReader(filename)) {
                var headers = reader.ReadLine();
                return reader.ReadLine();
            }
        };
    }

    public static void Main(string[] args) {
        var Reader = GetColumnReader("Input.tsv");
        Console.WriteLine(Reader());
    }


}

明らかに、これにより、返されたデリゲートを呼び出すたびに、ファイルが開かれたり、1行が読み取られたり、ファイルが閉じられたりします。

一度開いてから、数行を読んでいる間開いたままにする必要がある場合は、次のようなイテレータブロックを使用することをお勧めします。

public class Play {

    static IEnumerable<string> ReadLines(string filename) {
        using (TextReader reader = new StreamReader(filename)) {
            var headers = reader.ReadLine(); // I'm guessing you want to ignore this??
            while (true) {
                string line = reader.ReadLine();
                if (line == null)
                    yield break;
                yield return line;
            }
        }
    }

    public static void Main(string[] args) {
        foreach (string line in ReadLines("Input.tsv"))
            Console.WriteLine(line);
    }

}
于 2012-07-20T00:19:38.887 に答える
0

クロージャのセマンティクスを本当に保持したい場合は、引数を追加する必要があります。以下のようなものですが、disposeコマンドの呼び出しには注意が必要です。

public class Play {
    enum ReaderCommand {
        Read,
        Close
    }

    delegate string ColumnReader(ReaderCommand cmd);

    static ColumnReader GetColumnReader(string filename) {
        TextReader reader = new StreamReader(filename);
        var headers = reader.ReadLine();
        return (ReaderCommand cmd) => {
            switch (cmd) {
                case ReaderCommand.Read:
                    return reader.ReadLine();

                case ReaderCommand.Close:
                    reader.Dispose();
                    return null;
            }

            return null;
        };
    }

    public static void Main(string[] args) {
        var Reader = GetColumnReader("Input.tsv");
        Console.WriteLine(Reader(ReaderCommand.Read));
        Console.WriteLine(Reader(ReaderCommand.Read));
        Reader(ReaderCommand.Close);
        Console.ReadKey();
    }
}
于 2012-07-20T00:28:28.097 に答える
0

これは、単にTextReaderを返すよりも簡単です。特定のコーディングスタイルを実現するためだけに、物事をはるかに複雑にしているように思われます。

正しく返されたものをすべて処理する責任は、常に呼び出し元にあります。あなたのプロジェクトはあなたにあなたの筋肉を曲げるたくさんの機会を与えると確信しています-今回はそれを単純にしてください!

于 2012-07-20T01:23:09.880 に答える
0

私はyieldソリューションが本当に好きです。私は簡単なコードを書きました、それはそれがうまく機能することを示しています、リソースはクライアントが外れた後に処分することができます-それぞれ。

static void Main(string[] args)
{
    using (Resource resource = new Resource())
    {
       foreach (var number in resource.GetNumbers())
       {
          if (number > 2)
             break;
          Console.WriteLine(number);
       }
     }
     Console.Read();
 }
 public class Resource : IDisposable
 {
    private List<int> _numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 };

    public IEnumerable<int> GetNumbers()
    {
       foreach (var number in _numbers)
          yield return number;
    }

    public void Dispose()
    {
       Console.WriteLine("Resource::Dispose()...");
    }
 }
于 2012-07-20T02:10:36.947 に答える