web-dev-qa-db-ja.com

破棄する必要があることを知って、メソッドからストリームを返す方法は?

FileStreamを入力として受け取るメソッドがあります。このメソッドはforループ内で実行されています。

private void UploadFile(FileStream fileStream)
{
    var stream = GetFileStream();
    // do things with stream
}

FileStreamを作成して返す別のメソッドがあります。

private FileStream GetFileStream()
{
    using(FileStream fileStream = File.Open(myFile, FileMode.Open))
    {
        //Do something
        return fileStream;
    }
}

現在、返されたFileStreamにアクセスしようとすると、最初のメソッドがObjectDisposedExceptionをスローします。おそらく、ストリームを適切に破棄するために "using"を使用しているため、すでに閉じられているためです。

「using」を使用せず、代わりに次のように使用する場合、FileStreamは開いたままで、ループの次の反復(同じファイルで動作)は、ファイルがすでに使用中であることを示す例外をスローします。

private FileStream GetFileStream()
{
    FileStream fileStream = File.Open(myFile, FileMode.Open);
    //Do something
    return fileStream;
}

finallyのストリームを閉じるtry-finallyブロックを使用すると、ObjectDisposedExceptionもスローされます。

効果的にファイルストリームを返して閉じる方法は?

22
Frank Martin

メソッドからIDisposableを返す場合、それを破棄する責任を呼び出し元に任せています。したがって、ストリームの使用全体を囲んでusingブロックを宣言する必要があります。この場合、おそらくUploadFile呼び出しにまたがります。

using (var s = GetFileStream())
    UploadFile(s);
29
Douglas

問題は、GetFileStream()メソッドを終了するとすぐにFileStreamオブジェクトが破棄され、使用できない状態になることです。他の回答がすでに示しているように、そのメソッドからusingブロックを削除し、代わりにこのメソッドを呼び出すコードをusingブロックで囲む必要があります。

_private FileStream GetFileStream()
{
    FileStream fileStream = File.Open(myFile, FileMode.Open);
    //Do something
    return fileStream;
}

using (var stream = GetFileStream())
{
    UploadFile(stream);
}
_

しかし、私はこれをさらに一歩進めたいと思います。ずさんなプログラマーがusingブロックなしでメソッドを呼び出す場合、または少なくとも何らかの方法で結果を呼び出し側に強く示す場合から、GetFileStream()によって作成されたストリームを保護する方法が必要です。このメソッドのusingブロックで囲む必要があります。したがって、私はこれをお勧めします:

_public class FileIO : IDisposable
{
    private FileStream streamResult = null;

    public FileStream CreateFileStream(string myFile)
    {
        streamResult = File.Open(myFile, FileMode.Open);
        //Do something
        return streamResult;
    }

    public void Dispose()
    { 
       if (streamResult != null) streamResult.Dispose();         
    }

}

using (var io = FileIO())
{
    var stream = io.CreateFileStream(myFile);

    // loop goes here.
}
_

このために、必ずしもまったく新しいクラスを作成する必要はありません。 IDisposableコードを追加するだけで、このメソッドに適切なクラスが既にある場合があります。主なことは、このコードをIDisposableブロックでラップする必要があるという他のプログラマへのシグナルとしてusingを使用できることです。

さらに、これにより、クラスを変更してループの前にIDisposableオブジェクトを1回作成し、ループの最後に破棄する必要があるすべてを新しいクラスインスタンスに追跡させることができます。

12
Joel Coehoorn

開いているファイルストリームを返す必要があるメソッドがある場合、そのメソッドを呼び出す前にストリームを破棄できないため、そのメソッドのすべての呼び出し元は、返されたストリームを破棄する必要があります。

5
Servy