代码之家  ›  专栏  ›  技术社区  ›  Eric

是否需要将streamwriter包装在using块中?

c#
  •  21
  • Eric  · 技术社区  · 17 年前

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

    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 回复  |  直到 17 年前
        1
  •  22
  •   kemiller2002    17 年前

    不,在第一个示例中,流将保持打开状态,因为错误会使其关闭无效。

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

        2
  •  16
  •   Joe White    17 年前

    我要提出不同意见。特定问题的答案是“是否需要将streamwriter包装在using块中?”实际上是 不。 事实上,你 不应该 调用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)) { ... }
    

    因为当流作者 using block处理streamwriter,而streamwriter将反过来丢弃该流。因此,当您尝试从流中读取时,会得到一个ObjectDisposedException。

    流作者严重违反了“清理自己的烂摊子”规则。它试图清理别人的烂摊子,不管他们是否愿意。

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

    出于这个原因,我认为streamwriter(和streamreader,做同样的事情)是极少数的类,其中“如果它实现IDisposable,您应该调用Dispose”是错误的。 从未 对在现有流上创建的StreamWriter调用Dispose。改为调用flush()。

    然后,只要确保你在应该的时候清理这条小溪就行了。(正如Joe指出的那样,ASP.NET为您处理response.outputstream,因此您不必在这里担心它。)

    警告:如果不处置streamwriter,则 完成写入后需要调用flush()。否则,您可能仍然在内存中缓冲数据,而这些数据永远不会进入输出流。

    我对streamreader的规则是,假设它不实现IDisposable。完成后就放了。

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

        3
  •  4
  •   Fredrik Mörk    17 年前

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

    虽然您自己可以很好地编写这段代码,但是将它放入一个using块却容易得多。

        4
  •  3
  •   Nick    17 年前

    如果在没有使用块的情况下发生异常并终止程序,则会留下OpenConnections。使用块将始终为您关闭连接,类似于如果要使用try catch finally

        5
  •  3
  •   Adam Wright    17 年前

    最后,作者将被清理干净。当发生这种情况时,垃圾收集器会注意到尚未调用该命令的Dispose,并调用它。当然,GC可能不会运行几分钟、几小时或几天,这取决于具体情况。如果编写器对某个文件持有独占锁,那么即使您已经完成了很长时间,其他进程也无法打开该文件。

    using块确保始终进行Dispose调用,因此始终调用Close,而不管发生什么控制流。

        6
  •  1
  •   Jamie Ide    17 年前

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

        7
  •  1
  •   n8wrl    17 年前

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

        8
  •  0
  •   Robert    17 年前

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

        9
  •  0
  •   John Saunders    17 年前

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

        10
  •  0
  •   to StackOverflow    17 年前

    正如其他人指出的那样,总是使用一次性类(如streamwriter)是很好的做法,但在这种情况下,这并不重要。

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

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