Search code examples
c#.netidisposableusing-statement

Is there a way to have a using statement with dynamically-created targets?


Let's say I've defined a class MyDisposable : IDisposable. I know I can provide a hard-coded list of IDisposable objects to the using statement:

using (MyDisposable firstDisposable = new MyDisposable(),
    secondDisposable = new MyDisposable())
{
    // do something
}

Now let's say I have a few methods that perform operations on a collection of my disposable objects, and I want to do this inside a using statement. It might look like this (but this doesn't work of course because the using block expects one or more IDisposable objects and I'm passing a single collection object):

using (var myDisposables = GetMyDisposables())
{
    foreach (var myDisposable in myDisposables)
    {
        DoSomething(myDisposable);
        DoSomethingElse(myDisposable);
    }
}

Here are the other methods just for clarity:

static List<MyDisposable> GetMyDisposables()
{
    throw new NotImplementedException(); // return a list of MyDisposable objects
}

static void DoSomething(MyDisposable withMyDisposable)
{
    // something
}

static void DoSomethingElse(MyDisposable withMyDisposable)
{
    // something else
}

Is there some way I can accomplish this with the using statement? Or do I do just have to throw out the statement and manually dispose?


Solution

  • One approach that you can take is to make a collection of IDisposable objects, which is also IDisposable:

    class CollectionOfDisposable<T> : IDisposable where T : IDisposable  {
        public IList<T> Members {get; private set;}
        public CollectionOfDisposable(IEnumerable<T> members) {
            Members = members.ToList();
        }
        public void Dispose() {
            var exceptions = new List<Exception>();
            foreach (var item in Members) {
                try {
                    item.Dispose();
                } catch (Exception e) {
                    exceptions.Add(e);
                }
            }
            if (exceptions.Count != 0) {
                throw new AggregateException(exceptions);
            }
        }
    }
    

    Now you can write this:

    using (var myDisposables = GetMyDisposables()) {
        foreach (var myDisposable in myDisposables.Members) {
            DoSomething(myDisposable);
            DoSomethingElse(myDisposable);
        }
    }
    
    static CollectionOfDisposable<MyDisposable> GetMyDisposables() {
        throw new NotImplementedException(); // return a list of MyDisposable objects
    }