web-dev-qa-db-ja.com

Flush()なしでファイルストリームを閉じます

Flush (C#の場合)を呼び出さずにファイルストリームを閉じることはできますか? Close および Dispose が最初にFlushメソッドを呼び出すことを理解しました。

25
HelpME

MSDNは100%明確ではありませんが、Jon Skeetは「フラッシュ」と言っているので、閉じる/破棄する前にそれを行ってください。痛くないですよね?

からFileStream.Closeメソッド

以前にバッファに書き込まれたデータは、ファイルストリームが閉じる前にファイルにコピーされるため、Closeを呼び出す前にFlushを呼び出す必要はありません。 Closeの呼び出しに続いて、ファイルストリームに対する操作によって例外が発生する可能性があります。 Closeが一度呼び出された後、再度呼び出されても何も起こりません。

Disposeはそれほど明確ではありません。

このメソッドは、バッキングストアに変更を書き込み、ストリームを閉じてリソースを解放することにより、ストリームを破棄します。

注釈:コメンテーターは正しいかもしれません、それはフラッシュから100%明確ではありません:

バッファを実装するストリームのフラッシュをオーバーライドします。このメソッドを使用して、基礎となるバッファーからその宛先に情報を移動するか、バッファーをクリアするか、またはその両方を行います。オブジェクトの状態によっては、ストリーム内の現在の位置を変更する必要がある場合があります(たとえば、基になるストリームがシークをサポートしている場合)。詳細については、CanSeekを参照してください。

StreamWriterまたはBinaryWriterクラスを使用する場合は、基本のStreamオブジェクトをフラッシュしないでください。代わりに、クラスのFlushまたはCloseメソッドを使用します。これにより、データが最初に基になるストリームにフラッシュされてから、ファイルに書き込まれるようになります。

テスト:

var textBytes = Encoding.ASCII.GetBytes("Test123");
using (var fileTest = System.IO.File.Open(@"c:\temp\fileNoCloseNoFlush.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes,0,textBytes.Length);
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseNoFlush.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes, 0, textBytes.Length);
    fileTest.Close();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileFlushNoClose.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes, 0, textBytes.Length);
    fileTest.Flush();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseAndFlush.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes, 0, textBytes.Length);
    fileTest.Flush();
    fileTest.Close();
}

何と言えば...すべてのファイルがテキストを取得しました-これはデータが少なすぎるかもしれません。

テスト2

var rnd = new Random();
var size = 1024*1024*10;
var randomBytes = new byte[size];
rnd.NextBytes(randomBytes);
using (var fileTest = System.IO.File.Open(@"c:\temp\fileNoCloseNoFlush.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseNoFlush.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
    fileTest.Close();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileFlushNoClose.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
    fileTest.Flush();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseAndFlush.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
    fileTest.Flush();
    fileTest.Close();
}

そして再び-すべてのファイルはそのバイトを取得しました...私にはそれが私がMSDNから読んだことをしているように見えます:破棄する前にFlushまたはCloseを呼び出しても関係ありません...それについて何か考えはありますか?

17
Carsten

あなたはFlush()Close()/Dispose()で呼び出す必要がなくFileStreamがそれを行いますソースコードからわかるように、あなたのために:

http://referencesource.Microsoft.com/#mscorlib/system/io/filestream.cs,e23a38af5d11ddd

    [System.Security.SecuritySafeCritical]  // auto-generated
    protected override void Dispose(bool disposing)
    {
        // Nothing will be done differently based on whether we are 
        // disposing vs. finalizing.  This is taking advantage of the
        // weak ordering between normal finalizable objects & critical
        // finalizable objects, which I included in the SafeHandle 
        // design for FileStream, which would often "just work" when 
        // finalized.
        try {
            if (_handle != null && !_handle.IsClosed) {
                // Flush data to disk iff we were writing.  After 
                // thinking about this, we also don't need to flush
                // our read position, regardless of whether the handle
                // was exposed to the user.  They probably would NOT 
                // want us to do this.
                if (_writePos > 0) {
                    FlushWrite(!disposing); // <- Note this
                }
            }
        }
        finally {
            if (_handle != null && !_handle.IsClosed)
                _handle.Dispose();

            _canRead = false;
            _canWrite = false;
            _canSeek = false;
            // Don't set the buffer to null, to avoid a NullReferenceException
            // when users have a race condition in their code (ie, they call
            // Close when calling another method on Stream like Read).
            //_buffer = null;
            base.Dispose(disposing);
        }
    }
12
Dmitry Bychenko

ストリームが破棄されるときに.NET 4が変更をディスクに確実にフラッシュしないことを示すように見える、新しく導入されたバグを追跡してきました(.NET 2.0および3.5は常にそうでしたが、そうではありませんでした)。

.NET 4 FileStreamクラスはheavlyが.NET 4で変更され、Flush *()メソッドは書き直されましたが、.Dispose()についても同様の注意が忘れられているようです。

これにより、ファイルが不完全になります。

4
Lilith River

あなたがあなたがclose&disposeがflushメソッドを呼び出したことを理解したがユーザーコードによって明示的に呼び出されなかった場合、私は(フラッシュせずにcloseによって)実際に可能性を望んでいると信じている- discard必要に応じてFileStreamに加えられた変更。

それが正しい場合は、FileStreamだけを使用しても効果はありません。このファイルをMemoryStream(またはその内容の変更方法に応じて配列)にロードし、完了後に変更を保存するかどうかを決定する必要があります。

これの問題は明らかにファイルサイズです。 FileStreamは、限られたサイズの書き込みバッファーを使用して操作を高速化しますが、それらが枯渇すると、変更をフラッシュする必要があります。 .NETのメモリ制限により、完全に保持する必要がある場合にのみ、メモリに小さなファイルをロードすることが期待できます。

より簡単な代替案は、ファイルのディスクcopyを作成し、プレーンFileStreamを使用して作業することです。完了したら、変更を破棄する必要がある場合は、一時ファイルを削除するだけです。それ以外の場合は、元のファイルを変更したコピーで置き換えます。

3
Groo