是否有比嵌套"使用"更好的确定性处置方式?

时间:2020-03-05 18:56:51  来源:igfitidea点击:

在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.
}

Cto中有更好的方法吗?还是我坚持多层嵌套?

解决方案

回答

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

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

我们可以在对象上显式调用Dispose,但是这样做并不安全,因为如果其中之一引发异常,则资源将无法正确释放。

回答

除了使用语句嵌套之外,我们还可以手动写出.Dispose调用,但是几乎可以肯定在某个时候会错过一个。

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

回答

我们不必嵌套多个用法:

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
}

回答

我们可以将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

回答

我们可以使用以下语法将内容压缩一些:

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

这是罕见的情况之一,在所有块中不使用{}会引起恕我直言。

回答

我们可以省略花括号,例如:

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
}

回答

这使得代码行的净值增加了很多,但是可读性却得到了明显的提高:

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可以重构为更通用和可重用。

回答

我以前已经实现了像Michael Meadows的解决方案,但是如果在成员变量上调用的Dispose()方法由于某种原因或者随后的Dispose()原因引发异常,则他的StreamWrapper代码不会考虑在内。 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);
        }
    }

回答

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

using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
    // all three get disposed when you're done
}