关闭文件流时不使用Flush()方法

28

我可以在C#中关闭文件流而不调用Flush吗?我知道CloseDispose会先调用Flush方法。


1
为什么你想这样做呢?如果你不刷新,你就不知道文件里有什么,没有什么 - 所以你会在一个未定义的状态下关闭它。 - tdammers
1
你的意思是即使关闭流,也不想刷新它吗? - Oded
你的意思是什么?你想要省略手动刷新(这是可能的,因为Close/Dispose调用Flush),还是想要避免自动刷新(不可能,而且也不是好主意)? - CodesInChaos
Close 方法确实会刷新 FileStream 的缓冲区,但不会刷新任何具有单独缓冲区的 StreamReader/Writer 相关的缓冲区。只要您在读取数据,就没有问题,但如果您使用具有自己缓冲区的 StreamWriter 或任何其他包装器,那么您将遇到麻烦。 - Alois Kraus
1
Environment.FailFast() 可以使用。通过反射从 FileStream 中挖掘私有的 _handle 字段并使用 pinvoke 的 CloseHandle() 也可以使用,但你需要捕获 Close() 抛出的异常。 - Hans Passant
可能是重复的问题 *StreamWriter.Flush() 和 StreamWriter.Close() 有什么区别?*。 - Peter Mortensen
7个回答

19

你不需要在调用Close()/Dispose()时调用Flush(),如你从源代码中可以看到的一样,FileStream会为你自动执行:

http://referencesource.microsoft.com/#mscorlib/system/io/filestream.cs,e23a38af5d11ddd3

    [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);
        }
    }

17

MSDN的文档并不是100%清楚,但Jon Skeet建议在关闭/释放之前使用"Flush"方法。这样做不会有任何坏处,对吧?

来自FileStream.Close Method

在关闭文件流之前将缓冲区中先前写入的任何数据复制到文件中,因此在调用Close之前不需要调用Flush。在调用Close一次后,对文件流的任何操作都可能引发异常。如果再次调用Close,则不会执行任何操作。

Dispose的说明并不是很清晰:

通过将更改写入后备存储器并关闭流以释放资源来处理该流。

注:评论者可能是正确的,从Flush的说明并不是100%清楚:

在实现缓冲区的流上重写Flush。使用此方法将底层缓冲区中的任何信息移动到其目的地,清除缓冲区或两者皆可。根据对象的状态,您可能必须修改流中的当前位置(例如,如果底层流支持寻址)。有关详细信息,请参见CanSeek。

当使用StreamWriter或BinaryWriter类时,请勿刷新基础流对象。相反,请使用该类的Flush或Close方法,该方法确保首先将数据刷新到底层流,然后写入文件。

TESTS:

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上读到的事情:无论你在dispose之前调用Flush或Close都没关系...对此有什么想法吗?


1
以我所理解的方式,如果不刷新,你无法关闭它。即使你没有显式调用它,它也会自动为你刷新。 - Joshua Evensen
9
是的 - 你可以调用Close()而不调用Flush(),但是你不能在不刷新的情况下关闭 :) - Jon Skeet
3
诊所里一个诵读困难的牙医建议:在结束之前,请先冲洗(牙齿)。 - kasperhj
MSDN处理程序模式指南将提供更多的见解。特别是这部分:“如果在该领域中Close是标准术语,则考虑提供方法Close(),除了Dispose()之外。在这样做时,重要的是使Close实现与Dispose相同”(我强调)。因此,在using语句的结尾调用Close完全是多余的。你实际上是连续调用Close两次。(而且它们都调用Flush!) - AnorZaken
(在编程中)写得越少,你更有可能丢失数据,因为据我所知,缓冲区实现时会在满时自动刷新。不过这个还是要自己验证一下。 - AnorZaken
显示剩余2条评论

5
我一直在跟踪一个新引入的错误,它似乎表明当流被释放时,.NET 4不能可靠地将更改刷新到磁盘(不像.NET 2.0和3.5,它们总是可靠地这样做)。
.NET 4的FileStream类已经进行了大量修改,虽然Flush*()方法已经被重写,但是类似的注意事项似乎已经被忘记了。这导致文件不完整。

2
发现并修复了什么问题吗? - Peter

5

既然你已经明白了如果用户代码没有显式调用flush方法,close和dispose会自动调用它,那么我认为(通过不调用flush的close),你实际上想有一个可能性来丢弃FileStream所做的更改,如果必要的话。

如果是这样的话,仅使用FileStream是不够的。您需要将此文件加载到MemoryStream(或数组中,具体取决于您如何修改其内容)中,然后在完成后决定是否要保存更改。

问题在于文件大小,显然。 FileStream使用有限大小的写入缓冲区以加快操作速度,但一旦它们被耗尽,就需要刷新更改。由于.NET内存限制,如果需要完全保存文件,则只能在内存中加载较小的文件。

一个更简单的替代方案是创建文件的磁盘副本,并使用普通的FileStream进行操作。完成后,如果需要丢弃更改,只需删除临时文件;否则,用修改后的副本替换原始文件。


0

FileStream包装在BufferedStream中,并在缓冲流之前关闭文件流。

var fs = new FileStream(...);
var bs = new BufferedStream(fs, buffersize);

bs.Write(datatosend, 0, length);

fs.Close();
try {
    bs.Close();
}
catch (IOException) {
}

-1

在大循环中使用Flush()是值得的。当你需要在一个循环内读写一个大文件时,这样做是有意义的。否则,如果缓冲区或计算机足够大,则在关闭之前不进行Flush()也没有关系。

例如:你需要读取一个大文件(以一种格式)并将其写入.txt文件中。

 StreamWriter sw =  ....    // using StreamWriter
// you read the File  ...
// and now you want to write each line for this big File using WriteLine ();


for ( .....)    // this is a big Loop because the File is big and has many Lines

{

 sw.WriteLine ( *whatever i read* );  //we write here somrewhere ex. one .txt anywhere

 sw.Flush();  // each time the sw.flush() is called, the sw.WriteLine is executed

}

sw.Close();

在这里非常重要的是使用Flush(); 因为否则每个writeLine都保存在缓冲区中,直到缓冲区满或程序达到sw.close();才会写入。

我希望这有助于更好地理解Flush的功能。


欢迎来到StackOverflow,感谢您的回答,但它并没有真正回答“在关闭之前是否需要手动刷新”的问题。 - Deanna

-2

我认为使用简单的using语句是安全的,在调用GetBytes()后会关闭流;

public static byte[] GetBytes(string fileName)
{
    byte[] buffer = new byte[4096];
    using (FileStream fs = new FileStream(fileName)) 
    using (MemoryStream ms = new MemoryStream())
    {
        fs.BlockCopy(ms, buffer, 4096); // extension method for the Stream class
        fs.Close();
        return ms.ToByteArray();
    }
}

8
你为什么在中间调用了 Close 函数? - Jon Skeet
@JonSkeet:因为他犯了错误,我想。 - apocalypse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接