Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting rid of nested using(...) statements

Sometimes I need to use several disposable objects within a function. Most common case is having StreamReader and StreamWriter but sometimes it's even more than this.

Nested using statements quickly add up and look ugly. To remedy this I've created a small class that collects IDisposable objects and disposes of them when it itself is disposed.

public class MultiDispose : HashSet<IDisposable>, IDisposable
{
    public MultiDispose(params IDisposable[] objectsToDispose)
    {
        foreach (IDisposable d in objectsToDispose)
        {
            this.Add(d);
        }
    }

    public T Add<T>(T obj) where T : IDisposable
    {
        base.Add(obj);
        return obj;
    }

    public void DisposeObject(IDisposable obj)
    {
        obj.Dispose();
        base.Remove(obj);
    }


    #region IDisposable Members

    public void Dispose()
    {
        foreach (IDisposable d in this)
        {
            d.Dispose();
        }

    }

    #endregion
}

So my code now looks like this:

        using (MultiDispose md = new MultiDispose())
        {
            StreamReader rdr = md.Add(new StreamReader(args[0]));
            StreamWriter wrt = md.Add(new StreamWriter(args[1]));
            WhateverElseNeedsDisposing w = md.Add(new WhateverElseNeedsDisposing());

            // code
        }

Is there anything wrong with this approach that can cause problems down the road? I left the Remove function inherited from the HashSet on purpose so that the class would be more flexible. Surely misusing this function can lead to objects not being disposed of properly, but then there many other ways to shoot yourself in the foot without this class.

like image 756
Ghostrider Avatar asked May 16 '10 19:05

Ghostrider


People also ask

What is using() in C#?

The using statement causes the object itself to go out of scope as soon as Dispose is called. Within the using block, the object is read-only and can't be modified or reassigned. A variable declared with a using declaration is read-only.

Why using statement in C#?

In C#, the using keyword has two purposes: The first is the using directive, which is used to import namespaces at the top of a code file. The second is the using statement. C# 8 using statements ensure that classes that implement the IDisposable interface call their dispose method.

Can we use nested using in C#?

In C#, nesting of for, while, and do-while loops are allowed and you can also put any nested loop inside any other type of loop like in a for loop you are allowed to put nested if loop.

Does using Dispose C#?

C# provides a special "using" statement to call Dispose method explicitly. using statement gives you a proper way to call the Dispose method on the object. In using statement, we instantiate an object in the statement. At the end of using statement block, it automatically calls the Dispose method.


3 Answers

You could just do this:

using (var a = new A())
using (var b = new B())
{
    /// ...
}
like image 130
ChaosPandion Avatar answered Oct 24 '22 10:10

ChaosPandion


A few points about the general principle:

  • Your code is distinctly non-idiomatic C#. Basically you're asking anyone else who works with your code to take on board an unusual style for very little benefit.
  • As others have pointed out, you can nest using statements without extra braces
  • If you find yourself with lots of using statements in a single method, you might want to consider breaking it into smaller methods
  • If you have two variables of the same type, you can use a single using statement:

    using (Stream input = File.OpenRead("input.dat"),
           output = File.OpenWrite("output.dat"))
    {
    }
    

Now assuming you really want to go ahead with this:

  • Your code will dispose of its contained resources in a hard-to-predict order. Instead of using a set, it should embed a list - and then dispose of things in the reverse order to the calls to Add.
  • There is no reason to derive from HashSet<T> or indeed any collection. You should just have a list within the class as a private member variable.
  • If one of the Dispose calls fails, none of the other Dispose calls will be made; with a traditional using statement, each call to Dispose is made within its own finally block.

Basically, I think it's a bad idea. Nesting two levels deep is far from painful; nesting three should be rare; nesting four or more strongly suggests refactoring. Rather than trying to cope with the pain of deep nesting, you should design away from it.

like image 44
Jon Skeet Avatar answered Oct 24 '22 11:10

Jon Skeet


Maybe it is just that you have shown a simple example, but I think the following is more readable.

 using (StreamReader rdr = new StreamReader(args[0])) 
 using (StreamWriter wrt = new StreamWriter(args[1])) 
 {     
   // code 
 }
like image 13
Chris Taylor Avatar answered Oct 24 '22 10:10

Chris Taylor