使用()和dispose()处理多个包装流

3
我理解你的意思是,只有在执行例如...时,你才需要使用最外层流的using(),是吗?
MemoryStream mstr = new MemoryStream();

using(StreamWriter w = new StreamWriter(mstr)) {
    ....
}

由于处理 StreamWriter 时也会处理/关闭基础流,因此不需要执行此操作?:

using(MemoryStream mstr = new MemoryStream())
using(StreamWriter w = new StreamWriter(mstr)) {
    ....
}

(请注意,这只是一些示例,用于处理包装流的方法,并非寻求替代方法,例如仅使用StringWriter等。)
(注意:这些只是举例说明如何处理包装流,不要像仅使用StringWriter那样寻找其他替代方案。)
5个回答

10

从可读性和可维护性的角度来看,将所有依赖资源放在它们自己的using块中是一个好主意。例如,在下面的代码中,最后一个大括号后,尝试访问mstr是不可能的,因为它的作用域仅限于其有效的块内:

using (MemoryStream mstr = new MemoryStream())
using (StreamWriter w = new StreamWriter(mstr) {
    ....
}

// cannot attempt to access mstr here
如果你不像这样限定它的作用范围,那么在其有效范围之外依然可以访问mstr
MemoryStream mstr = new MemoryStream();
using (StreamWriter w = new StreamWriter(mstr) {
    ....
}

mstr.Write(...); // KABOOM! ObjectDisposedException occurs here!

尽管这并不总是必要的(在这种情况下确实不是必要的),但这是一个好主意,因为它可以澄清和强制执行你关于其范围的意图。


这确实是一个更好的解释。 - Stan R.

7

我的经验法则:如果它实现了IDisposable接口,就要对其进行处理。

尽管目前(也许永远),调用StreamWriter.Dispose()会关闭底层流,但是您将来可能使用的其他派生自流的类可能不会这样做。此外,似乎它也没有实际调用Dispose(),因此非MemoryStreams可能无法得到正确的处理(尽管我现在想不出任何会受到影响的流)。

因此,虽然您可以安全地仅处置StreamWriter,但我发现每当可能时始终使用using块来处理可处置对象是更好的实践。


2
Stream.Close() 表示“此方法调用Dispose,指定为true以释放所有资源。”换句话说,在流上调用Close()会自动调用Dispose(true)。 - Powerlord
@R Bemrose,正如我所说,StreamWriter在底层流上调用close(而不是dispose)。底层流调用内部dispose的事实是一种实现细节,即使它当前可以工作,我认为依赖于所有流的这种实现链都是一种不好的做法。就个人而言,我会处理任何实现IDisposable接口的东西,并且这就是我提出的经验法则。再次强调,这并非必须的。 - Philip Rieck
调用Dispose通常具有与调用Close相同的效果,以及其他效果,例如抑制finalize。因此,使用“using”而不是“Close”是安全的。 - Steven Sudit

4
从Reflector中查看StreamWriter.Dispose方法,似乎底层流被关闭了,但没有被处理。为了明确处理所有流,我会将每个流放入"using"块中以便显式地进行处理。

1
实际上,在基础流上调用.Close()会调用this.Dispose(),因此将自身处理掉。 - Stan R.
2
这是它今天的功能,Stan。我不认为这是合同的一部分。 - John Saunders
@John:我只是在说从“查看反编译器”来看,Close确实会调用Dispose。我并不是建议使用它,我只是说出了反编译器实际显示的内容。 - Stan R.
@Stan R:我的担忧是,有些人读到这篇文章可能无法理解这个区别,因为像您这样的专家将问题转给了 Reflector。 - John Saunders
4
@John:Stream.Close 的文档说明是释放流正在使用的任何资源:http://msdn.microsoft.com/en-us/library/system.io.stream.close.aspx,这可能包括其他流。 - Powerlord
显示剩余2条评论

3
回答您的实际问题。TextWriter的Dispose方法。
public void Dispose()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

它调用了一个受保护的Dispose方法,并将true传递给它。来源:TextWriter.Dispose Method (Boolean) 当disposing参数为true时,该方法释放此TextWriter引用的所有托管对象所占用的资源。此方法会调用每个引用对象的Dispose方法。
然而,最佳实践是将所有实现IDisposable的内容都包装在using块中,因为我们无法保证受保护的Dipose方法总是使用true参数被调用。

0

在编程中,最好的实践是始终使用using块。

“始终”指的是除了众所周知的WCF代理类之外。由于设计缺陷,它们的Dispose方法有时会引发异常,从而丢失原始异常。


从未遇到过这个错误?链接连接文章? - Greg D

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