你能阻止StreamReader释放底层流吗?

64

有没有一种方法可以做到这一点:

this.logFile = File.Open("what_r_u_doing.log", FileMode.OpenOrCreate, FileAccess.ReadWrite);

using(var sr = new StreamReader(this.logFile))
{
    // Read the data in
}

// ... later on in the class ...

this.logFile = File.Open("what_r_u_doing.log", FileMode.OpenOrCreate, FileAccess.ReadWrite);

using(var sw = new StreamWriter(this.logFile))
{
    // Write additional data out...
}

有没有不用打开两次文件的方法?

我似乎无法使StreamReader不释放我的流。 我也不想让它超出范围,因为垃圾收集器最终会调用Dispose来关闭流。

9个回答

87

.NET 4.5将在StreamReader和StreamWriter上提供新的构造函数,包括一个名为"leaveOpen"的参数,最终解决了这个问题:

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

StreamWriter(Stream stream, System.Text.Encoding encoding, int bufferSize, bool leaveOpen)

27
问题在于这迫使我要弄清楚传递哪种编码才能让StreamReader的行为与使用“StreamReader(Stream)”构造函数打开它时的行为相同。因此,我仍然采用“不处理并希望我的同事不会通过将StreamReader放入using块来‘更正’该方法”的方法:( - phoog
19
@phoog:(赶上我的收件箱)- MSDN 应该列出转发样式重载的默认值,例如 StreamReader(Stream) 表示:“此构造函数将编码初始化为 UTF8Encoding,使用流参数初始化 BaseStream 属性,并将内部缓冲区大小设置为 1024 字节。” - Simon Buchan
9
但我不应该在我的程序中硬编码这些默认值。StreamReader() 构造函数应该被修改为接受一个类似于 XmlReaderSettings 的选项式类参数。然后默认值可以由框架管理,并以非破坏 ABI 和非硬编码的方式添加新参数。 - binki
2
使用以下代码可以保持默认设置:using(var reader = StreamReader(OtherStream, System.Text.Encoding.UTF8, 1024, true)) { ... } 注释已移除 - TamusJRoyce
2
使用命名参数可以避免手动指定默认值的需要:new StreamReader(stream, leaveOpen: true)。 - Phil Dennis
显示剩余5条评论

53

我也不想让它超出范围,否则垃圾收集器最终会调用Dispose方法,关闭流。

垃圾收集器将调用Finalize方法(析构函数),而不是Dispose方法。终结器将调用Dispose(false),它不会释放底层流。如果您需要直接使用底层流,则可以让StreamReader超出范围而无需担心。只需确保在适当的时候手动处理底层流。


17
你可以使用Jon Skeet的MiscUtil库中的NonClosingStreamWrapper类,它正好可以实现这个目的。

这是 Jon Skeet 的 NonClosingStreamWrapper 的 .NET Standard 2.1 版本的 gist - Angel Yordanov

3
您可以创建一个继承自StreamReader的新类,并重写Close方法;在您的Close方法内,调用Dispose(false),这不会关闭流。当然,对于StreamWriter也是一样的。
但是,更好的解决方案似乎是只要需要就保留StreamReader和StreamWriter实例。如果您已经计划保持流处于打开状态,那么最好也保持StreamReader和StreamWriter处于打开状态。如果您正确使用了StreamWriter.Flush和Stream.Seek,即使同时进行读取和写入,也应该能够使其正常工作。

2

只需删除using-Block。如果您不想Dispose()流,则无需Dispose() StreamReader。


3
但是在未来,StreamReader是否有可能被修改以持有某些非托管资源,并开始实现终结器,然后在垃圾回收器进行终结时随机关闭你的流?或者我们应该假设,既然现在没有发生这种情况,那么它永远不会发生? - binki

2
使用另一个构造函数重载,其中您可以指定“leaveOpen”参数为“true”。

1
我通常使用这样的方法: (它还使用了 leaveOpen 参数)
public static class StreamreaderExtensions
{
    public static StreamReader WrapInNonClosingStreamReader(this Stream file) => new StreamReader(file, Encoding.UTF8, true, 1024, true);
}

使用方法:

using (var reader = file.WrapInNonClosingStreamReader())
{
     ....
}

1
根据参考源,这似乎是一个很好的答案。它相当于调用Stream无参数构造函数,但指定leaveOpen为true。 - Elliott Beach

1
我能够使用 leaveOpen 参数,而不必像下面这样指定所有构造函数参数(编码或缓冲区大小):
using var streaReader = new StreamReader(stream, leaveOpen: true);

没有提供与所需的形式参数“encoding”相对应的参数。 (.NET 4.8) - Mr. L
这对我来说可行。这是一个伟大的、简单和有表现力的解决方案。 - ViRuSTriNiTy

0
在你完成使用后,可以在try/finally子句中自行关闭它。
var sr = new StreamReader();
try {
    //...code that uses sr
    //....etc
}
finally
{
    sr.Close();
}

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