Is there a better deterministic disposal pattern than nested “using”s?

故事扮演 提交于 2019-12-04 00:02:49

You don't have to nest with multiple usings:

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
}

You can put using statements together before the opening braces like so:

  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

You could use this syntax to condense things down a bit:

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

This is one of those rare occasions where not using { } for all blocks makes sense IMHO.

Instead of nesting using statements, you can just write out the .Dispose calls manually - but you'll almost certainly miss one at some point.

Either run FxCop or something else that can make sure that all IDisposable-implementing type instances have a .Dispose() call, or deal with the nesting.

Jesse C. Slicer

I have implemented solutions like Michael Meadows's before, but his StreamWrapper code doesn't take into account if the Dispose() methods called on the member variables throw an exception for one reason or another, the subsequent Dispose()es will not be called and resources could dangle. The safer way for that one to work is:

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

you can omit the curly braces, like:

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

or use the regular try finally approach:

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
}

This makes for a much larger net plus in lines of code, but a tangible gain in readability:

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

Where StreamWrapper is defined here:

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

With some effort, StreamWrapper could be refactored to be more generic and reusable.

It should be noted that generally when creating stream based off another stream the new stream will close the one being passed in. So, to further reduce your example:

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

for this example let us assume you have:

a file named 1.xml under c:\

a textbox named textBox1, with the multi-line properties set 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();

The using statement is syntactic sugar that converts to:

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

You can explicitly call Dispose on your objects, but it won't be as safe, since if one of them throws an exception, the resources won't be freed properly.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!