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

时间:2009-06-19 16:51:37

标签: c#

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

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

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

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

10 个答案:

答案 0 :(得分:22)

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

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

答案 1 :(得分:16)

我要发表不同意见。特定问题的答案“是否有必要将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。)

答案 2 :(得分:4)

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();    
    }
}

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

答案 3 :(得分:3)

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

答案 4 :(得分:3)

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

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

虽然像其他人指出的那样总是将一次性类(例如StreamWriter)放弃,但在这种情况下并不重要。

在完成处理请求后,ASP.NET基础架构将处理Response.OutputStream。

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