Click here to Skip to main content
11,573,432 members (60,281 online)
Click here to Skip to main content

Using "using" Statements: DisposalAccumulator

, 25 Feb 2013 CPOL 27.1K 24
Rate this:
Please Sign up or sign in to vote.
Helps to deal with irregular construction patterns of disposable objects

Epigraph:

Thanks to everyone; everyone is dismissed.

Favorite film director's phrase, according to some movies about movie making Smile | <img src=

Introduction

This tip is based on my own practice but was recently inspired by my CodeProject colleague Mika Wendelius who posed a very interesting question in the CodeProject Questions & Answers forum.

Using a pattern based on the interface System.IDisposable and the C# using statement, he managed to face a very simple but representative case where using could dramatically damage the code reuse. I answered on the question on the same day, pointing out that the solution really depends on the relationships between classes involved in the instantiation under the using statement's resource acquisition part (the class declarations were not shown in the question). If two of the classes are related by inheritance, and one of the classes uses a late-bound constructor parameter, one pretty obvious resolution is possible without any repeated code fragments. I demonstrated this solution based on the assumption of having inheritance and late binding in those IDisposable classes. I also basically explained how a solution for a general case could look, but did not provide any code for that, because I think that Mika is more than enough qualified to get the idea and do everything by himself. Anyone can see this answer here.

The problem with the remarkable Mika's example is rooted in the clash between having dynamic (run-time) dependency between classes or structures instantiated under the using resource acquisition and the using construct structure being defined statically and frozen by compile time. When Mika introduces a condition affecting the dependency in the object graph, it broke the static using structure.

I have a solution for this problem, but the exotic Mika's example is not the only case where this solution can be used. It was something I never thought of before (again, a wonderful example), but there are many more trivial examples which would make using just poorly readable and hence a subject of developer's mistakes. One such trivial case would be the one with the need of excessive using blocks nesting; a simple case where there are just too many disposable objects could also make using of using somewhat problematic.

That's why I think the simple technique I'm going to show could be of some interest to many members.

The Problem

I've slightly modified Mika's sample, to make it a bit simpler but to manifest the problem in a bit more dramatic way.

Consider the following method with two different nested using statements. I need this “naive” code sample to expose the problem:

static void NaiveWay(bool createSecond) {
    if (createSecond)
        using (First first = new First()) {
            using (Second second = new Second(first)) {
                using (Third third = new Third(second)) {
                    third.DoSomething();
                } //disposing third
            } //disposing second
        } //disposing first
    else //oh, no! :-) the code reuse is lost:
        using (First first = new First()) {
            using (Third third = new Third(first)) {
                third.DoSomething();
            } //disposing third
        } //disposing first
} //NaiveWay

Apparently, this “naive” violates one of the most fundamental principles of programming: “Don't repeat yourself”. Such repeated code fragments is always a problem for code maintenance and hence they invite human mistakes and threaten code reliability. The two code fragments with different using structures should be somehow merged together, but how?

Why the two if branches would be needed? This is because the different values of the Boolean parameters dictate different nesting of using blocks, not just the structure of the object graph. As I pointed out in my answer referenced above, different class declarations and different object graphs can require such different using structures. Normally, different forms of the object graph depending on some conditions do not present any problems, but instantiation in the resource acquisition part of the using statements (please see the ISO/IEC 23270 of 09/01/2006 standard document, section 15.3, “The using statement”) creates the problem.

In one case, all three classes could be not related by inheritance, but the different using structures could result from the fact that the class Third has two alternative constructors:

using System;

//...

class First : IDisposable {
    void IDisposable.Dispose() {/* ... */}
    //...
} //class First

class Second : IDisposable {
    internal Second(First child) {/* ... */}
    void IDisposable.Dispose() {/* ... */}
    //...
} //class Second

class Third : IDisposable {
    internal Third(First child) {/* ... */}
    internal Third(Second child) {/* ... */}
    internal void DoSomething() {/* ... */}
    void IDisposable.Dispose() {/* ... */}
    //...
} //class Third

Another variant of declaration of the classes could also use identical using structures shown above. This is the case where the class Third uses the single constructor with the late bound run-time type of the input parameter:

class First : IDisposable {
    void IDisposable.Dispose() {/* ... */}
    //...
} //class First

class Second : First, IDisposable {
    internal Second(First child) {/* ... */}
    void IDisposable.Dispose() {/* ... */}
    //...
} //class Second

class Third : IDisposable {
    internal Third(First child) {/* ... */}
    internal void DoSomething() {/* ... */}
    void IDisposable.Dispose() {/* ... */}
    //...
} //class Third

For the second case, I've demonstrated the solution shown in sample code in my answer referenced above. But this is a special case. How to overcome the difficulty explained in the present section above in a universal manner which would cover both cases of the object graph, as well as many more?

To approach the problem, let's remember that the purpose of using of the using statement the automatic call to the method System.IDisposable.Dispose and that it is strictly equivalent to some try-finally statement. (Please see this link.) Of course, if we try to write such equivalent try-finally statement, the problem of repeated code can be resolved. Let's see:

static void DontRepeatYourselfTryFinally(bool createSecond) {
    First first = null;
    Second second = null;
    Third third = null;
    try {
        first = new First();
        if (createSecond) {
            second = new Second(first);
            third = new Third(second);
        } else
            third = new Third(first);
        third.DoSomething();
    } finally {
        if (third != null) ((IDisposable)third).Dispose();
        if (second != null) ((IDisposable)second).Dispose();
        if (first != null) ((IDisposable)first).Dispose();
    } //exception
} //DontRepeatYourselfTryFinally

Is it a good solution? Well, it's pretty ugly, despite of getting rid of some repeated code. First of all, it requires type casting with all those brackets; and this is always ugly and error prone. Even more importantly, one clear benefit of using has gone: now we need to remember about both construction of objects and calling that Dispose method, which can easily be forgotten. It could be considered as better code or not, but certainly not as a good one.

The Solution

Let's finally admit that using just different variants of C# syntax is not enough. We need a helper class which can cover all the possible object construction patterns in a uniform manner. It does not even have to be generic, because it simply deals with types (classes, and structures as well, in boxed form) implementing the interface System.IDisposable. However, using its only public method is dramatically simplified if we make it generic. I called it DisposalAccumulator:

using IDisposable = System.IDisposable;
using System.Collections.Generic;

public class DisposalAccumulator : IDisposable {

    public T Add<T>(T element) where T : IDisposable {
        if (element != null)
            stack.Push(element);
        return element;
    } //Add

    void IDisposable.Dispose() {
        foreach (IDisposable element in stack)
            element.Dispose();
    } //IDisposable.Dispose

    Stack<IDisposable> stack = new Stack<IDisposable>();

} //class DisposalAccumulator

In essence, the class implements System.IDisposable.Dispose to dispose all its added elements. The Stack class is used to implement element storage, without extra overhead, with the most natural order of enumeration of elements used for disposal: in reverse, relative to the order of adding them.

Now, the usage is reduced to using of only one using statement, no ifs, no buts. It's used only for the instance of the DisposalAccumulator:

static void DontRepeatYourself(bool createSecond) {
    using (DisposalAccumulator accumulator = new DisposalAccumulator()) {
        First first = accumulator.Add(new First());
        Third third;
        if (createSecond) //no need to create a variable "second"
            third = accumulator.Add(new Third(accumulator.Add(new Second(first))));
        else
            third = accumulator.Add(new Third(first));
        third.DoSomething();
    } //everything is disposed
} //DontRepeatYourself 

Note the value of the generic Add method with the pass-through input parameter. Interestingly, type inference (available in .NET v.3.5 and later) allows not to instantiate this generic method with Add, as the compiler can figure out the required generic parameter from the compile-time type of a variable on the left of the assignment operator. It makes the usage especially smooth.

One may argue that the call to DisposalAccumulator.Add can easily be forgotten for some of the variables. My answer would be: that is not more likely than forgetting of using one of using statements; it usually happens when a developers fails to see that some library type implements System.IDisposable.

Conclusion

Overall, the nesting using statements, even without a run-time condition affecting their structures could be used if the number of disposable objects is reasonably small. In all other cases, using the DisposalAccumulator or a similar helper would be quite beneficial.

Credits

Of course, I appreciate inventiveness, the thirst for perfection, openness and collaborative attitudes of Mika Wendelius who caused me to get a very fresh look at the problem.

Andreas Gieriet found a bug in the last code fragment, usage sample. I fixed it. Thank you very much, Andi!

Robert Schutt advised an improvement to the code sample of DontRepeatYourselfTryFinally — thank you very much, Robert!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sergey Alexandrovich Kryukov
Architect
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Edo Tzumer25-Feb-13 19:16
memberEdo Tzumer25-Feb-13 19:16 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov25-Feb-13 19:35
mvpSergey Alexandrovich Kryukov25-Feb-13 19:35 
GeneralMy vote of 5 Pin
Marcus Kramer2-Nov-12 10:40
memberMarcus Kramer2-Nov-12 10:40 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov2-Nov-12 10:51
mvpSergey Alexandrovich Kryukov2-Nov-12 10:51 
GeneralRe: My vote of 5 Pin
Marcus Kramer2-Nov-12 10:58
memberMarcus Kramer2-Nov-12 10:58 
Questionput try/catch Pin
FatCatProgrammer18-Sep-12 7:20
memberFatCatProgrammer18-Sep-12 7:20 
AnswerRe: put try/catch Pin
Sergey Alexandrovich Kryukov18-Sep-12 10:00
mvpSergey Alexandrovich Kryukov18-Sep-12 10:00 
Thank you for these comments.

Apparently, one can sandwich the call do Dispose for logging purposes, but this goes somewhat beyond this topic. Besides, it's better to make it optional. Usually, it's assumed that Dispose is called in the finally section (via using or not), its implementation is simple and does not itself through exceptions.

I think making DisposalAggregator a structure is quite possible, could even be beneficial, but it won't make much difference: using it in using will cause its boxing anyway; besides, the collection member is stored on the stack anyway, so… no, not a big difference.

Explicit interface implementation? Well, yes, it is quite beneficial in many cases. This just provides better encapsulation and hiding. In the scope of the problem under consideration it is pretty obvious. What part of code should really use the knowledge that the type implements System.IDispose and call System.IDispose.Dispose directly, because this is a public method? Really none, it normally should happen via using. Making public more than it is really needed is always bad; the tighter the access limitations, the better.

Thank you,
—SA

Sergey A Kryukov

GeneralRe: put try/catch Pin
FatCatProgrammer18-Sep-12 12:51
memberFatCatProgrammer18-Sep-12 12:51 
GeneralRe: put try/catch Pin
Sergey Alexandrovich Kryukov18-Sep-12 14:02
mvpSergey Alexandrovich Kryukov18-Sep-12 14:02 
GeneralRe: put try/catch Pin
FatCatProgrammer18-Sep-12 14:46
memberFatCatProgrammer18-Sep-12 14:46 
GeneralRe: put try/catch Pin
Sergey Alexandrovich Kryukov18-Sep-12 14:57
mvpSergey Alexandrovich Kryukov18-Sep-12 14:57 
GeneralRe: put try/catch Pin
FatCatProgrammer18-Sep-12 15:16
memberFatCatProgrammer18-Sep-12 15:16 
GeneralRe: put try/catch Pin
Sergey Alexandrovich Kryukov18-Sep-12 16:29
mvpSergey Alexandrovich Kryukov18-Sep-12 16:29 
GeneralRe: put try/catch Pin
FatCatProgrammer19-Sep-12 0:46
memberFatCatProgrammer19-Sep-12 0:46 
GeneralRe: put try/catch Pin
Sergey Alexandrovich Kryukov19-Sep-12 8:23
mvpSergey Alexandrovich Kryukov19-Sep-12 8:23 
GeneralRe: put try/catch Pin
Sergey Alexandrovich Kryukov19-Sep-12 11:08
mvpSergey Alexandrovich Kryukov19-Sep-12 11:08 
GeneralRe: put try/catch Pin
FatCatProgrammer20-Sep-12 2:54
memberFatCatProgrammer20-Sep-12 2:54 
GeneralRe: put try/catch Pin
Sergey Alexandrovich Kryukov20-Sep-12 6:22
mvpSergey Alexandrovich Kryukov20-Sep-12 6:22 
GeneralRe: put try/catch Pin
FatCatProgrammer20-Sep-12 17:11
memberFatCatProgrammer20-Sep-12 17:11 
GeneralRe: put try/catch Pin
Sergey Alexandrovich Kryukov20-Sep-12 18:44
mvpSergey Alexandrovich Kryukov20-Sep-12 18:44 
GeneralExtending this idea Pin
torial18-Sep-12 4:55
membertorial18-Sep-12 4:55 
GeneralRe: Extending this idea Pin
Sergey Alexandrovich Kryukov18-Sep-12 10:03
mvpSergey Alexandrovich Kryukov18-Sep-12 10:03 
GeneralRe: Extending this idea Pin
torial21-Sep-12 3:55
membertorial21-Sep-12 3:55 
GeneralMy vote of 5 Pin
AdamDavidHill18-Sep-12 0:44
memberAdamDavidHill18-Sep-12 0:44 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov18-Sep-12 3:47
mvpSergey Alexandrovich Kryukov18-Sep-12 3:47 
GeneralMy vote of 5 Pin
robert_schutt17-Sep-12 4:02
memberrobert_schutt17-Sep-12 4:02 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov17-Sep-12 14:14
mvpSergey Alexandrovich Kryukov17-Sep-12 14:14 
GeneralThe call added (Re: My vote of 5) Pin
Sergey Alexandrovich Kryukov17-Sep-12 14:23
mvpSergey Alexandrovich Kryukov17-Sep-12 14:23 
GeneralMy vote of 5 Pin
DrABELL16-Sep-12 7:58
memberDrABELL16-Sep-12 7:58 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov16-Sep-12 15:38
mvpSergey Alexandrovich Kryukov16-Sep-12 15:38 
GeneralMy vote of 5 Pin
Mika Wendelius14-Sep-12 7:56
mvpMika Wendelius14-Sep-12 7:56 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov14-Sep-12 8:15
mvpSergey Alexandrovich Kryukov14-Sep-12 8:15 
SuggestionA slightly different approach.... Pin
Andrew Rissing14-Sep-12 5:19
memberAndrew Rissing14-Sep-12 5:19 
GeneralRe: A slightly different approach.... Pin
Andrew Rissing14-Sep-12 5:22
memberAndrew Rissing14-Sep-12 5:22 
GeneralRe: A slightly different approach.... Pin
Sergey Alexandrovich Kryukov14-Sep-12 6:29
mvpSergey Alexandrovich Kryukov14-Sep-12 6:29 
QuestionMy 5! Pin
Andreas Gieriet13-Sep-12 22:11
memberAndreas Gieriet13-Sep-12 22:11 
AnswerRe: My 5! Pin
Sergey Alexandrovich Kryukov14-Sep-12 6:30
mvpSergey Alexandrovich Kryukov14-Sep-12 6:30 
GeneralMy vote of 5 Pin
azweepay13-Sep-12 21:51
memberazweepay13-Sep-12 21:51 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov14-Sep-12 6:37
mvpSergey Alexandrovich Kryukov14-Sep-12 6:37 
GeneralMy vote of 5 Pin
Eddy Vluggen13-Sep-12 21:22
memberEddy Vluggen13-Sep-12 21:22 
GeneralRe: My vote of 5 Pin
Sergey Alexandrovich Kryukov14-Sep-12 6:37
mvpSergey Alexandrovich Kryukov14-Sep-12 6:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150624.2 | Last Updated 26 Feb 2013
Article Copyright 2012 by Sergey Alexandrovich Kryukov
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid