20

在 C# 中,如果我想确定性地清理非托管资源,我可以使用“using”关键字。但是对于多个依赖对象,这最终会越来越嵌套:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
{
    using (BufferedStream bs = new BufferedStream(fs))
    {
        using (StreamReader sr = new StreamReader(bs))
        {
            // use sr, and have everything cleaned up when done.
        }
    }
}

在 C++ 中,我习惯于使用析构函数来做到这一点:

{    
    FileStream fs("c:\file.txt", FileMode.Open);
    BufferedStream bs(fs);
    StreamReader sr(bs);
    // use sr, and have everything cleaned up when done.
}

在 C# 中有没有更好的方法来做到这一点?还是我坚持多层嵌套?

4

10 回答 10

40

您不必嵌套多个使用:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
    // all three get disposed when you're done
}
于 2008-09-16T19:01:51.180 回答
8

您可以将 using 语句放在左大括号之前,如下所示:

  using (StreamWriter w1 = File.CreateText("W1"))
  using (StreamWriter w2 = File.CreateText("W2"))
  {
      // code here
  }

http://blogs.msdn.com/ericgu/archive/2004/08/05/209267.aspx

于 2008-09-16T19:02:29.230 回答
3

您可以使用此语法将内容压缩一下:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
}

恕我直言,这是对所有块不使用 { } 的罕见情况之一。

于 2008-09-16T19:02:31.647 回答
1

您可以手动编写 .Dispose 调用,而不是嵌套 using 语句 - 但您几乎肯定会在某些时候错过一个。

运行 FxCop 或其他可以确保所有 IDisposable 实现类型实例都有 .Dispose() 调用的东西,或者处理嵌套。

于 2008-09-16T19:01:41.417 回答
1

我之前已经实现了类似Michael Meadows的解决方案,但是他的StreamWrapper代码没有考虑到如果在Dispose()成员变量上调用的方法由于某种原因引发异常,Dispose()则不会调用后续的 es 并且资源可能会悬空。更安全的工作方式是:

        var exceptions = new List<Exception>();

        try
        {
            this.sr.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        try
        {
            this.bs.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        try
        {
            this.fs.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        if (exceptions.Count > 0)
        {
            throw new AggregateException(exceptions);
        }
    }
于 2008-09-16T20:25:40.760 回答
0

您可以省略花括号,例如:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
        // use sr, and have everything cleaned up when done.
}

或使用常规的 try finally 方法:

FileStream fs = new FileStream("c:\file.txt", FileMode.Open);
BufferedStream bs = new BufferedStream(fs);
StreamReader sr = new StreamReader(bs);
try
{
        // use sr, and have everything cleaned up when done.
}finally{
   sr.Close(); // should be enough since you hand control to the reader
}
于 2008-09-16T19:03:50.270 回答
0

这使得代码行数增加了很多,但在可读性方面有明显的提高:

using (StreamWrapper wrapper = new StreamWrapper("c:\file.txt", FileMode.Open))
{
    // do stuff using wrapper.Reader
}

StreamWrapper 在这里定义的地方:

private class StreamWrapper : IDisposable
{
    private readonly FileStream fs;
    private readonly BufferedStream bs;
    private readonly StreamReader sr;

    public StreamWrapper(string fileName, FileMode mode)
    {
        fs = new FileStream(fileName, mode);
        bs = new BufferedStream(fs);
        sr = new StreamReader(bs);
    }

    public StreamReader Reader
    {
        get { return sr; }
    }

    public void Dispose()
    {
        sr.Dispose();
        bs.Dispose();
        fs.Dispose();
    }
}

通过一些努力,可以将 StreamWrapper 重构为更通用和可重用。

于 2008-09-16T19:08:55.190 回答
0

应该注意的是,通常在基于另一个流创建流时,新流将关闭正在传入的流。因此,为了进一步减少您的示例:

using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
    // all three get disposed when you're done
}
于 2008-09-16T23:32:15.023 回答
0

对于这个例子,让我们假设你有:

c:\ 下名为 1.xml 的文件

一个名为 textBox1 的文本框,其多行属性设置为 ON。

const string fname = @"c:\1.xml";

StreamReader sr=new StreamReader(new BufferedStream(new FileStream(fname,FileMode.Open,FileAccess.Read,FileShare.Delete)));
textBox1.Text = sr.ReadToEnd();
于 2011-05-20T19:48:01.063 回答
-1

using 语句是语法糖,可转换为:

   try
   {
      obj declaration
      ...
   }
   finally
   {
      obj.Dispose();
   }

您可以在对象上显式调用 Dispose,但它不会那么安全,因为如果其中一个抛出异常,资源将不会被正确释放。

于 2008-09-16T19:01:13.850 回答