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

Eri*_*ric 22 c#

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

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

有人告诉我,在异常的情况下,我应该将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();
Run Code Online (Sandbox Code Playgroud)

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

kem*_*002 22

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

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


Joe*_*ite 16

我要发表不同意见.特定问题的答案"是否有必要将StreamWriter包装在一个使用块中?" 实际上是事实上,你不应该叫上一个StreamWriter处置,因为它的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)) { ... }
Run Code Online (Sandbox Code Playgroud)

因为当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.)

  • .Net 4.5修复了这个疏忽,它现在允许你选择让流打开:http://msdn.microsoft.com/en-us/library/gg712853.aspx (12认同)