Ian Goldby
Ian Goldby

Reputation: 6174

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.

Upvotes: 0

Views: 94

Answers (2)

Robert Giesecke
Robert Giesecke

Reputation: 4314

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)
  {

  }

Upvotes: 1

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249686

While I don't see a way to avoid the creator function, you can make it generic enough to define once and use for any class:

static T WithStream<T>(string path, Func<FileStream, T> getter)
{
  using (FileStream stream = File.Open(path, FileMode.Open))
  {
    return getter(stream);
  }
}

class MyObject : IDisposable
{
    public MyObject (Stream stream){ /* Work with stream */}
    public void Dispose(){}

}
static void Main()
{

    using (MyObject obj = WithStream("path", fs => new MyObject(fs)))
    {
      // do something with obj
    }
}

Upvotes: 1

Related Questions