0

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.

6
  • you dont need a using block at all, you could just create the stream, pass to MyObject, then call stream.Dispose(). Or course, you'll need to wrap this in a try catch Commented Jul 5, 2018 at 14:08
  • 2
    What would be the point of writing it like that? Why do you only need the stream in the constructor? Commented Jul 5, 2018 at 14:08
  • 2
    You're aware that obj = ...; using (obj) { .. } is a thing, right? You are not required to initialize the variable and wrap it in one single block. If you're concerned about obj possibly not being disposed when an exception occurs, just use try .. finally yourself instead of using. Commented Jul 5, 2018 at 14:08
  • Why is the stream being passed to the constructor? Commented Jul 5, 2018 at 14:11
  • 1
    If you don't need the stream for the whole time, why it needs to be passed as constructor argument? Maybe the constructor should not take the stream but what you want to read with it. Or it could at take the file-path and create and use it there. Is MyObject supposed to be the reader-class or just the class that holds the informations which you want to read? It should not be used for both because that are two separate concerns. Commented Jul 5, 2018 at 14:12

2 Answers 2

1

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

  }
Sign up to request clarification or add additional context in comments.

6 Comments

Wouldnt the stream be disposed by the time you get to using(obj)?
@maccettura That's the point. You want the stream closed as soon as the MyObject constructor has completed.
Forget the 'magic'. The second bit of code is exactly what I was flailing so hopelessly around for. Thanks. (Can I suggest you edit the 1st solution out of your answer?)
@IanGoldby I don’t think he should. He’s making a very good point that the best way is the more readable way. The other solutions here and elsewhere on this page are arguably nothing more than an excercise in cleverness at the expense of readability
@MickyD You mean keep the 'clever' but less clear solution as a counterpoint to the no-nonsense good solution? Not sure I agree - a clear and simple solution stands on its own merit.
|
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.