Search code examples
c#usingidisposable

Avoiding nested using blocks when outer is used only to construct inner


Often, the IDisposable object of a using block is itself constructed from another IDisposable object, e.g.

using (FileStream stream = File.Open(path, FileMode.Open))
using (MyObject obj = new MyObject(stream))
{
  // do something with obj
}

Unfortunately the code above keeps the file stream open until the MyObject object is disposed.

To dispose of the file stream as soon as the MyObject constructor has completed, I could instead write:

MyObject CreateMyObject(string path)
{
  using (FileStream stream = File.Open(path, FileMode.Open))
  {
    return new MyObject(stream);
  }
}

using (MyObject obj = CreateMyObject(path))
{
  // do something with obj
}

But I don't like the verbosity of this solution. I tried replacing CreateMyObject() with a lambda but I failed to find a legal syntax. Is there a way to do this without calling a custom creator function?


Edit: Bearing in mind some of the comments, I should point out that I'm trying to avoid try...finally - kind of the main reason for a using block in the first place.

Additional clarification: The MyObject object is constructed from information in the stream - i.e. its constructor reads the content of the stream in its entirity. No other method in MyObject references the stream. The content of the stream could come from anywhere - a file, a resource, an Internet socket, etc.


Solution

  • You could invoke some magic like so:

    TResult CreateUsingDisposable<TDisposable, TResult>(TDisposable disposable, Func<TDisposable, TResult> getResult)
      where TDisposable : IDisposable
    {
      using (disposable)
      {
        return getResult(disposable);
      }
    }
    
    using (var obj = CreateUsingDisposable(new FileStream(path, FileMode.Open), stream => new MyObject(stream)))
    {
    }
    

    But why? There's a super easy to read no-nonsense way of doing that:

      MyObject obj;
      using (var stream = new FileStream(path, FileMode.Open))
      {
        obj = new MyObject(stream);
      }
      using (obj)
      {
    
      }