Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could one refactor code involved in nested usings?

I've got some code that has an awful lot of duplication. The problem comes from the fact that I'm dealing with nested IDisposable types. Today I have something that looks like:

public void UpdateFromXml(Guid innerId, XDocument someXml)
{
    using (var a = SomeFactory.GetA(_uri))
    using (var b = a.GetB(_id))
    using (var c = b.GetC(innerId))
    {
        var cWrapper = new SomeWrapper(c);
        cWrapper.Update(someXml);
    }
}

public bool GetSomeValueById(Guid innerId)
{
    using (var a = SomeFactory.GetA(_uri))
    using (var b = a.GetB(_id))
    using (var c = b.GetC(innerId))
    {
        return c.GetSomeValue();
    }
}

The whole nested using block is the same for each of these methods (two are shown, but there are about ten of them). The only thing that is different is what happens when you get to the inner level of the using blocks.

One way I was thinking would be to do something along the lines of:

public void UpdateFromXml(Guid innerId, XDocument someXml)
{
    ActOnC(innerId, c =>
    { 
        var cWrapper = new SomeWrapper(c);
        cWrapper.Update(someXml);
    });
}

public bool GetSomeValueById(Guid innerId)
{
    var result = null;

    ActOnC(innerId, c => { result = c.GetSomeValue(); });

    return result;
}

private void ActOnC(Guid innerId, Action<TheCType> action)
{
    using (var a = SomeFactory.GetA(_uri))
    using (var b = a.GetB(_id))
    using (var c = b.GetC(innerId))
    {
        action(c);
    }        
}

This works, it's just kind of clunky to parse (as a human). Does anyone have any other suggestions on how one could reduce the code duplication around nested using blocks like this? If they weren't IDisposable then one would likely just create a method to return the results of b.GetC(innerId) ... but that isn't the case here.

like image 738
ckittel Avatar asked Apr 23 '12 17:04

ckittel


1 Answers

I like the answer provided by BFree as a start, but I'd make a few modifications.

//Give it a better name; this isn't designed to be a general purpose class
public class MyCompositeDisposable : IDisposable 
{
    public MyCompositeDisposable (string uri, int id, int innerid)
    {
        A = SomeFactory.GetA(uri);
        B = A.GetB(id);
        C = B.GetC(innerId);
    }

    //You can make A & B private if appropriate; 
    //not sure if all three or just C should be exposed publicly.
    //Class names are made up; you'll need to fix.  
    //They should also probably be given more meaningful names.
    public ClassA A{get;private set;}
    public ClassB B{get;private set;}
    public ClassC C{get;private set;}

    public void Dispose()
    {
        A.Dispose();
        B.Dispose();
        C.Dispose();
    }
}

After doing that you can do something like:

public bool GetSomeValueById(Guid innerId)
{
    using(MyCompositeDisposable d = new MyCompositeDisposable(_uri, _id, innerId))
    {
        return d.C.GetSomeValue();
    }
}

Note that the MyCompositeDisposable will likely need to have try/finally blocks in the constructor and Dispose methods so that errors in creation/destruction properly ensure nothing ends up un-disposed.

like image 140
Servy Avatar answered Nov 16 '22 22:11

Servy