30

I've noticed that the level of nested using statements has lately increased in my code. The reason is probably because I use more and more of async/await pattern, which often adds at least one more using for CancellationTokenSource or CancellationTokenRegistration.

So, how to reduce the nesting of using, so the code doesn't look like Christmas tree? Similar questions have been asked on SO before, and I'd like to sum up what I've learnt from the answers.

Use adjacent using without indentation. A fake example:

using (var a = new FileStream())
using (var b = new MemoryStream())
using (var c = new CancellationTokenSource())
{
    // ... 
}

This may work, but often there's some code between using (e.g. it may be too early to create another object):

// ... 
using (var a = new FileStream())
{
    // ... 
    using (var b = new MemoryStream())
    {
        // ... 
        using (var c = new CancellationTokenSource())
        {
            // ... 
        }
    }
}

Combine objects of the same type (or cast to IDisposable) into single using, e.g.:

// ... 
FileStream a = null;
MemoryStream b = null;
CancellationTokenSource c = null;
// ...
using (IDisposable a1 = (a = new FileStream()), 
    b1 = (b = new MemoryStream()), 
    c1 = (c = new CancellationTokenSource()))
{
    // ... 
}

This has the same limitation as above, plus is more wordy and less readable, IMO.

Refactor the method into a few methods.

This is a preferred way, as far as I understand. Yet, I'm curious, why would the following be considered a bad practice?

public class DisposableList : List<IDisposable>, IDisposable
{
    public void Dispose()
    {
        base.ForEach((a) => a.Dispose());
        base.Clear();
    }
}

// ...

using (var disposables = new DisposableList())
{
    var a = new FileStream();
    disposables.Add(a);
    // ...
    var b = new MemoryStream();
    disposables.Add(b);
    // ...
    var c = new CancellationTokenSource();
    disposables.Add(c);
    // ... 
}

[UPDATE] There are quite a few valid points in the comments that nesting using statements makes sure Dispose will get called on each object, even if some inner Dispose calls throw. However, there is a somewhat obscure issue: all nested exceptions possibly thrown by disposing of nested 'using' frames will be lost, besides the most outer one. More on this here.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    You can try techniques like method extraction. I mean try to divide this particular method into small independent parts and move them into methods. This way you might be able to move this multiple `using` blocks into different methods. – Hossain Muctadir Oct 07 '13 at 05:41
  • 3
    Usually, if you use more than, let's say, 2 nested `using` statements, your method is a bit too complex anyway, so refactoring is required anyway. If you more ore less follow the 'clean code' principle you usually don't end up in nesting too many `using` statements. @MuctadirDinar: Same thoughts! – alzaimar Oct 07 '13 at 05:42
  • 1
    I typically find that 3 is about the most I'll ever nest, and I find the normal nested indentation perfectly readable, and clearer than any of the other alternatives you bring up. Maybe after 4 or 5 it might get a little screwy, but even then, I'd rather have obvious code that's a little bit long than a non-standard pattern to research when I'm reading the code. Monitors these days are generally pretty wide, so I wouldn't worry too much about horizontal space. – Joe Enos Oct 07 '13 at 06:10
  • Yep. Consider refactoring in the extreme case. The code usually requires comments anyway and refactoring (split into methods) is the ideal way to both comment the code and make it more readable. – alzaimar Oct 07 '13 at 06:25
  • 2
    3 is clearly bad: it requires special effort to not forget to dispose object while writing code and produces unusual code that is much harder to read. Side note: as it is show in question 3 variant suffer from chance of never disposing some objects if earlier `Dispose` throws an exception. – Alexei Levenkov Oct 07 '13 at 06:28
  • this question sounds like a much better fit at programmers or even codereview... – Michael Edenfield Oct 07 '13 at 07:52
  • @AlexeiLevenkov, interestingly enough though, while nested `using` statements do call `Dispose` on each object, [some exceptions may just get lost](http://stackoverflow.com/q/19238521/1768303). – noseratio Oct 08 '13 at 03:31
  • When I have two nested using like the second example in the question, I get a warning CA2202: Do not dispose objects multiple times on the closing brace of the outmost using. I have enabled Code Analysis in Visual Studio 2017. – Angelo Mascaro Jul 09 '18 at 12:48
  • Related post - [Nested using statements in C#](https://stackoverflow.com/q/1329739/465053) – RBT Jan 23 '22 at 06:52

5 Answers5

16

In a single method, the first option would be my choice. However in some circumstances the DisposableList is useful. Particularly, if you have many disposable fields that all need to be disposed of (in which case you cannot use using). The implementation given is good start but it has a few problems (pointed out in comments by Alexei):

  1. Requires you to remember to add the item to the list. (Although you could also say you have to remember to use using.)
  2. Aborts the disposal process if one of the dispose methods throws, leaving the remaining items un-disposed.

Let's fix those problems:

public class DisposableList : List<IDisposable>, IDisposable
{
    public void Dispose()
    {
        if (this.Count > 0)
        {
            List<Exception> exceptions = new List<Exception>();

            foreach(var disposable in this)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (Exception e)
                {
                    exceptions.Add(e);
                }
            }
            base.Clear();

            if (exceptions.Count > 0)
                throw new AggregateException(exceptions);
        }
    }

    public T Add<T>(Func<T> factory) where T : IDisposable
    {
        var item = factory();
        base.Add(item);
        return item;
    }
}

Now we catch any exceptions from the Dispose calls and will throw a new AggregateException after going through all the items. I've added a helper Add method that allows a simpler usage:

using (var disposables = new DisposableList())
{
    var file = disposables.Add(() => File.Create("test"));
    // ...
    var memory = disposables.Add(() => new MemoryStream());
    // ...
    var cts = disposables.Add(() => new CancellationTokenSource());
    // ... 
}
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • Thanks for the great point about aggregating exceptions and a neat piece of code on its own, especially factoring. – noseratio Oct 07 '13 at 07:31
  • Interestingly, aggregating exceptions is different from the standard behavior of nested `using`, where [some exceptions may apparently get lost](http://stackoverflow.com/q/19238521/1768303). – noseratio Oct 08 '13 at 03:21
  • @Noseratio I'm not surprised. That is the behavior defined in the [spec](http://stackoverflow.com/questions/2911215/what-happens-in-c-sharp-if-a-finally-block-throws-an-exception). That is often very hard to debug because the exception you see is not the cause of the problem. I'd argue the behavior above will actually make it easier to debug such problems if they do arise. – Mike Zboray Oct 08 '13 at 03:47
  • 1
    @Noseratio Actually strike my last sentence. The `AggregateException` could still hide the source of your problems. Since we have no access to the exception that caused us to enter the list's `Dispose`, you need to decide whether it is worth it to swallow the `Dispose` exceptions or not. I agree with Marc Gravell's statement [here](http://stackoverflow.com/a/577620/517852), that generally the original exception is the interesting one. I would at least try to log them in any case. – Mike Zboray Oct 08 '13 at 04:06
  • [Here](http://stackoverflow.com/a/19240514/1768303) is my attempt to avoid swallowing or replacing the inner exceptions, @mikez. – noseratio Oct 09 '13 at 02:32
6

You should always refer to your fake example. When this is not possible, like you mentioned, then it is very likely that you can refactor the inner content into a separate method. If this also does not make sense you should just stick to your second example. Everything else just seems like less readable, less obvious and also less common code.

user2674389
  • 1,123
  • 7
  • 8
6

I would stick to the using blocks. Why?

  • It clearly shows your intentions with these objects
  • You don't have to mess around with try-finally blocks. It's error prone and your code gets less readable.
  • You can refactor embedded using statements later (extract them to methods)
  • You don't confuse your fellow programmers including a new layer of abstractions by creating your own logic
Balázs Szántó
  • 1,440
  • 3
  • 15
  • 29
1

Your last suggestion hides the fact that a, b and c should be disposed explicitly. That`s why it's ugly.

As mentioned in my comment, if you'd use clean code principles you wouldn't run into these problems (usually).

alzaimar
  • 4,572
  • 1
  • 16
  • 30
  • I tend to agree, but please elaborate how is that different from my second code fragment (nested `using` each with curly braces)? Do you mean the separation makes the disposal more explicit? – noseratio Oct 07 '13 at 05:59
  • 1
    Try not to use tricks to avoid the nesting. Ask yourself: "Is it readable? Would my colleague understand the code without asking?" If the answer is 'yes' in both cases, everything is ok. If you tend to comment your code, split it. This technique avoids too many nesting levels in most cases. – alzaimar Oct 07 '13 at 06:22
1

Another option is to simply use a try-finally block. This might seem a bit verbose, but it does cut down unnecessary nesting.

FileStream a = null;
MemoryStream b = null;
CancellationTokenSource c = null;

try
{
   a = new FileStream();
   // ... 
   b = new MemoryStream();
   // ... 
   c = new CancellationTokenSource();
}
finally 
{
   if (a != null) a.Dispose();
   if (b != null) b.Dispose();
   if (c != null) c.Dispose();
}
codemonkeh
  • 2,054
  • 1
  • 20
  • 36
  • 6
    As you should use `using` I would not recommend this. This is a trick to avoid nesting, i.e. personal taste. Don't use tricks. There is actually nothing bad with nesting itself. – alzaimar Oct 07 '13 at 06:23
  • 2
    +0. Your suggestion have the same problem as option 3 in the question - it requires special effort to not forget to dispose object while writing code and produces unusual code that is much harder to read (in addition your suggestion encourages copy-paste which adds risk of more errors). Both version suffer from chance of never disposing some objects if earlier `Dispose` throws an exception. – Alexei Levenkov Oct 07 '13 at 06:25
  • 1
    @alzaimar Why "should" one use a `using` statement? It is merely a short hard form of a try-finally block provided for convenience. The use of either is personal taste and i did not suggest nesting was a bad thing, I am merely replying to the OP's desire to do so. – codemonkeh Oct 07 '13 at 06:36
  • 2
    This is best option for me. As it clearly shows what is used, what required to be disposed. Also Using statement is just a Short Cut for try & Finally block. It just improves coding readability than try & finally. So its up to the programmer what is easy to read for him, and this is different for each programmer. – CreativeManix Oct 07 '13 at 08:55
  • I personnaly think this is better than the other suggestion (#3, #4) – Rémi Oct 07 '13 at 11:42
  • 1
    @codemonkeh: You "should" means only that you should, not must. It is easier to read and as the langugage provides this, why not use it? Using your argumentation, one should not have to use a `foreach` or a `for` loop, because they are only short forms of a `do..while` loop. But: It does eliminate the use of `using`. If somebody gets pimples while using `using`, your suggestion is the cure. In our team, he'd get beaten up, though ;-) – alzaimar Oct 07 '13 at 14:15
  • @alzaimar Just because a language provides a feature doesn't mean you should use it. Why use any for/foreach statements when I can just write most of them in linq? ;) What's important is having an understanding of what each feature does and to use it as is appropriate to the situation. Anything else is just "style" that will change over time and will always remain debatable. – codemonkeh Oct 07 '13 at 22:00
  • 4
    -1: Your method fails to clean up resources in the event that one of the earlier calls to `Dispose` throws an exception. You have defeated the entire purpose of the `using` statement. – Sam Harwell Oct 08 '13 at 04:37
  • @codemonkeh: I did not say, you should (in terms of 'have to'). But, it's there. If you don't like it, don't use it. But please, don't switch your way of argumentation each time you respond. – alzaimar Oct 08 '13 at 12:22
  • @alzaimar I don't see how I have? – codemonkeh Oct 08 '13 at 22:01
  • @codemonkeh: Last chance: First, you mention, that because `using` "is merely a short hand form of a try-finally-block". When I mention, that with that argumentation the same applies to `foreach` and `for` in relation to a `while` loop, you refuse and then switch your argumentation to readability (which is perfectly alright and a matter of taste). Got it? – alzaimar Oct 09 '13 at 05:39
  • @alzaimar I believe you misunderstood me, my argument has always been that you should understand how the framework works and use it as you deem appropriate. I have never stated that any feature or shortcut is a bad thing and if you missed my (poor) attempt at humour in a previous comment than I am sorry for you. Oh and if you don't believe me that the `using` statement compiles to a try-finally block then I suggest you look the MSIL that is generated. – codemonkeh Oct 09 '13 at 09:30