请考虑以下代码:
using (var ms = new MemoryStream())
{
using(var writer = BinaryWriter(ms))
{
writer.Write(/*something*/);
writer.Flush();
}
Assert.That(ms.Length > 0); // Throws ObjectDisposedException
}
一方面,一次性物品应该处理它的资源;我明白了,但另一方面,对象没有创建并且没有拥有这个资源,它提供了 - >调用代码应该对它负责......不是吗?
我无法想到这样的任何其他情况,但是对于任何接收一次性物品的类来自行处理它们的框架中它是否是一致的模式?
答案 0 :(得分:13)
有一个隐含的假设,即每个流只有一个编写器,因此为方便起见,编写者会假定流的所有权 - 然后您需要清理一件事。
但我同意;这并非总是如此,而且往往不方便。某些实现(例如DeflateStream,GZipStream)允许您选择。否则,唯一真正的选择是在编写器和底层流之间注入虚拟流; IIRC在Jon Skeet的“MiscUtil”库中有一个NonClosingStreamWrapper正是这样做的:http://www.yoda.arachsys.com/csharp/miscutil/
用法如下:
using (var ms = new MemoryStream())
{
using(var noClose = new NonClosingStreamWrapper(ms))
using(var writer = BinaryWriter(noClose))
{
writer.Write(/*something*/);
writer.Flush();
}
Assert.That(ms.Length > 0);
}
答案 1 :(得分:4)
我完全同意你的看法。这不是一致的行为,但它是如何实现的。文档末尾有关于此行为的comments,这不是很直观。所有流编写者只需拥有底层流的所有权并进行处理。就个人而言,我总是像这样嵌套using
语句:
using (var ms = new MemoryStream())
using(var writer = BinaryWriter(ms))
{
writer.Write(/*something*/);
}
这样就不应该写出像Assert那样的代码了。
答案 2 :(得分:0)
正确的做法是为streamwriter指定一个构造函数参数,指示在构造函数是否应该处理流。鉴于Microsoft没有这样做,最好将NonDisposingStream(Of T as Stream)类定义为包装流,但不将Dispose调用传递给包装流。然后可以将新的NonDisposingStream传递给StreamWriter的构造函数,并且可以安全地处理基础流(当然,有必要自行处理流)。
拥有一个可以处理传入对象的对象很有用。虽然这种行为与对象创建者处理其处置的通常模式不一致,但通常情况下,对象的创建者不知道对象实际需要多长时间。例如,可以期望一种方法创建使用新流的新StreamWriter。 StreamWriter的所有者将知道何时应该处理它,但可能不知道内部流的存在。内部流的创建者将不知道外部StreamWriter将被使用多长时间。拥有对StreamWriter“交付”流的所有权解决了该特定(常见)案例中的处理问题。
答案 3 :(得分:0)
我建议这个包装类:
public class BetterStreamWriter : StreamWriter
{
private readonly bool _itShouldDisposeStream;
public BetterStreamWriter(string filepath)
:base(filepath)
{
_itShouldDisposeStream = true;
}
public BetterStreamWriter(Stream stream)
: base(stream)
{
_itShouldDisposeStream = false;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing && _itShouldDisposeStream);
}
}
对象不应该处置他们没有实例化的东西。如果它是文件流编写器,它应该处理。如果它是外部流,则不应该。
首先不应该实现打开文件路径。这违反了单一责任原则,因为该对象既管理文件的写入和生命周期。