是否有必要将StreamWriter包装在一个使用块中?

几天前我发布了一些像这样的代码:

StreamWriter writer = new StreamWriter(Response.OutputStream); writer.WriteLine("col1,col2,col3"); writer.WriteLine("1,2,3"); writer.Close(); Response.End(); 

有人告诉我,在exception的情况下,我应该将StreamWriter包装在一个使用块中。 这样的改变会使它看起来像这样:

 using(StreamWriter writer = new StreamWriter(Response.OutputStream)) { writer.WriteLine("col1,col2,col3"); writer.WriteLine("1,2,3"); writer.Close(); //not necessary I think... end of using block should close writer } Response.End(); 

我不确定为什么这是一个有价值的变化。 如果在没有使用块的情况下发生exception,编写器和响应仍然会被清除,对吧? 使用块让我获得了什么?

在第一个例子中,流不会保持打开,因为错误会否定它的关闭。

使用运算符强制调用Dispose(),它应该向上清理对象并在它退出块时关闭所有打开的连接。

我要发表不同意见。 特定问题的答案“是否有必要将StreamWriter包装在一个使用块中?” 实际上,你不应该在StreamWriter上调用Dispose,因为它的Dispose设计糟糕并且做错了。

StreamWriter的问题在于,当您处理它时,它会释放底层流。 如果您使用文件名创建了StreamWriter,并且它在内部创建了自己的FileStream,那么这种行为将是完全合适的。 但是,如果在这里,您使用现有流创建了StreamWriter,那么这种行为绝对是错误的事情(tm)。 但无论如何它确实如此。

这样的代码不起作用:

 var stream = new MemoryStream(); using (var writer = new StreamWriter(stream)) { ... } stream.Position = 0; using (var reader = new StreamReader(stream)) { ... } 

因为当StreamWriter using块时会释放StreamWriter,这会反过来丢弃流。 因此,当您尝试从流中读取时,会出现ObjectDisposedException。

StreamWriter是一个可怕的违反“清理你自己的混乱”规则。 它试图清理别人的混乱,无论他们是否想要它。

(想象一下,如果你在现实生活中尝试过这种方法。试着向警察解释为什么你闯入别人的房子然后开始将所有东西扔进垃圾桶……)

出于这个原因,我认为StreamWriter(和StreamReader,它做同样的事情)是极少数类中的“如果它实现IDisposable,你应该调用Dispose”是错误的。 永远不要在现有流上创建的StreamWriter上调用Dispose。 调用Flush()代替。

然后确保在应该的时候清理Stream。 (正如Joe所指出的,ASP.NET为你配置了Response.OutputStream,所以你不必在这里担心它。)

警告:如果你不处理StreamWriter,那么你需要在写完后调用Flush()。 否则,您可能仍然在内存中缓冲数据,而这些数据从未进入输出流。

我对StreamReader的规则是,假装它没有实现IDisposable。 只要你完成就放手吧。

我对StreamWriter的规则是,调用Flush,否则你将调用Dispose。 (这意味着你必须使用try .. finally而不是using 。)

如果在没有使用块的情况下发生exception并杀死程序,则将保留openconnections。 使用块将始终为您关闭连接,类似于您是否使用try {} catch {} finally {}

最终,作家将被清理干净。 当发生这种情况时,由垃圾收集器决定,他们会注意到没有调用Dispose for the command,并调用它。 当然,根据具体情况,GC可能无法运行数分钟,数小时或数天。 如果作者对一个文件持有一个独占锁,即使你已经很久完成,也没有其他进程可以打开它。

using块确保始终进行Dispose调用,因此无论发生什么控制流,都始终调用Close。

StreamWriter包装在using块中几乎等同于以下代码:

 StreamWriter writer; try { writer = new StreamWriter(Response.OutputStream); writer.WriteLine("col1,col2,col3"); writer.WriteLine("1,2,3"); } catch { throw; } finally { if (writer != null) { writer.Close(); } } 

虽然您可以自己编写这些代码,但将它放在一个使用块中要容易得多。

在我看来,有必要在使用块中包装任何实现IDisposable的类。 类实现IDisposable的事实意味着该类具有需要清理的资源。

我的经验法则是,如果我看到intellisense中列出的Dispose,我将它包装在一个使用块中。

using块在结束时调用dispose()。 这只是确保及时清理资源的便捷方式。

几乎在每种情况下,如果一个类实现了IDisposable,并且如果你正在创建该类的实例,那么你需要使用using块。

虽然像其他人指出的那样总是抛弃像StreamWriter这样的一次性类是很好的做法,但在这种情况下并不重要。

Response.OutputStream将在处理完您的请求后由ASP.NET基础结构处理。

StreamWriter假设它“拥有”传递给构造函数的Stream,因此在它被处理时关闭流。 但是在您提供的示例中,流在代码之外被实例化,因此将有另一个负责清理的所有者。