部署streamreader是否关闭流?


166

我正在向要发送的方法发送流,在这些方法中,我使用的是二进制读取器/ wrtier。当读取器/写入器被处置using或被未引用处置时,流是否也被关闭?

我将发送BinaryReader / Writer,但我也使用StreamReader(也许我应该解决这个问题。我仅将其用于GetLine和ReadLine)。如果每次关闭写入器/读取器时都关闭流,这将很麻烦。

Answers:


204

是的,StreamReaderStreamWriter,当您调用它们时BinaryReaderBinaryWriter所有它们都会关闭/处置其基础流Dispose。如果读取器/写入器只是垃圾回收,则它们不会处理流-您应该始终处理读取器/写入器,最好使用using语句。(实际上,这些类都不具有终结器,也不应该具有终结器。)

就个人而言,我也希望对流也具有using语句。您可以using整齐地嵌套语句而无需大括号:

using (Stream stream = ...)
using (StreamReader reader = new StreamReader(stream, Encoding.Whatever))
{
}

即使using该流的语句有点多余(除非StreamReader构造函数抛出异常),我还是将其视为最佳实践,因为如果您摆脱了StreamReader并在以后直接使用该流,那么您已经可以正确处理语义。


2
哦,好,它只会在调用Dispose时发生,而不是在最终确定时发生。
夫岑2009年

1
@Nefzen:这是因为无法保证您的对象将按什么顺序完成。如果StreamReader和基础Stream都可以进行终结处理,则GC可能会首先终结该流-然后streamreader将没有对流的引用。因此,您只能在完成过程中释放非托管资源(例如,FileStream在其完成过程中关闭其Windows文件句柄)。哦,当然,如果您从不处理,流最终仍将被收集(并且文件已关闭)。不处理流是非常不好的做法。
JMarsch

13
这种嵌套导致VS代码分析器抱怨:CA2202 : Microsoft.Usage : Object 'stream' can be disposed more than once in method '...'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.是否应该忽略它?到目前为止,我没有任何例外...
HB

15
@HB:在这种情况下可以忽略它。或者,您可以只在的构造函数调用中创建流StreamReader。该警告对我来说似乎是虚假的,因为docs IDisposable.Dispose明确声明:“如果对象的Dispose方法被多次调用,则该对象必须在第一个调用之后忽略所有调用。如果对象的Dispose方法为被多次呼叫。”
乔恩·斯基特

5
@JonSkeet:实际上有一个页面,您是对的,这是假的:)
HB

45

这是一个旧的,但是我今天想做类似的事情,发现情况已经改变。从.net 4.5开始,有一个leaveOpen参数:

public StreamReader( Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen )

唯一的问题是,为其他参数设置什么并不完全明显。这里有一些帮助:

msdn页的StreamReader构造函数(Stream):

此构造函数将编码初始化为UTF8Encoding,使用stream参数将BaseStream属性初始化,并将内部缓冲区大小初始化为1024字节。

这只是叶detectEncodingFromByteOrderMarks其判断由源代码true

public StreamReader(Stream stream)
        : this(stream, true) {
}

public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
        : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}

如果公开了其中一些默认值,或者这些参数是可选的,那么我们就可以指定所需的默认值了。


非常好的信息!从来没有听说过这个新参数,它实际上很有意义。
julealgon

3
对于像我这样的懒惰的人,让信息流using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
畅通无阻

29

是的,它确实。您可以通过查看Reflector的实现来验证这一点。

protected override void Dispose(bool disposing)
{
    try
    {
        if ((this.Closable && disposing) && (this.stream != null))
        {
            this.stream.Close();
        }
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {    
            this.stream = null;    
            this.encoding = null;
            this.decoder = null;
            this.byteBuffer = null;
            this.charBuffer = null;
            this.charPos = 0;
            this.charLen = 0;
            base.Dispose(disposing);
        }
    }
}

13

迟了六年,但这也许会对某人有所帮助。

StreamReader在处理连接时会关闭连接。但是,在StreamReader / StreamWriter中“使用(Stream stream = ...){...}”可能导致Stream被处置两次:(1)处置StreamReader对象时(2)和Stream using块时关闭。运行VS的代码分析时,这会导致CA2202警告。

直接从CA2202页获得的另一种解决方案是使用try / finally块。正确设置,这将只关闭一次连接。

Microsoft建议在CA2202底部附近使用以下内容:

Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.OpenOrCreate);
    using (StreamWriter writer = new StreamWriter(stream))
    {
        stream = null;
        // Use the writer object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

代替...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}

2
警告也将在接受答案的注释中进行讨论。乔恩·斯基特(Jon Skeet)在此提供了一些建议。
Marcin

另外,请注意,using语句实际上是由编译器转换为try-finally块的。
杰森·凯利

2

是。在IDisposable和IDisposable上调用Dispose()(“使用”可以做到)应使对象清除其所有资源。这包括流刷新和关闭其文件描述符。

如果您希望将其传递给其他方法,则需要确保这些方法不会在using块中进行读取/写入。



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.