Epigraph:
Thanks to everyone; everyone is dismissed.
Favorite film director's phrase, according to some movies about movie making
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();
} } } else using (First first = new First()) {
using (Third third = new Third(first)) {
third.DoSomething();
} } }
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 Second : IDisposable {
internal Second(First child) {}
void IDisposable.Dispose() {}
}
class Third : IDisposable {
internal Third(First child) {}
internal Third(Second child) {}
internal void DoSomething() {}
void IDisposable.Dispose() {}
}
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 Second : First, IDisposable {
internal Second(First child) {}
void IDisposable.Dispose() {}
}
class Third : IDisposable {
internal Third(First child) {}
internal void DoSomething() {}
void IDisposable.Dispose() {}
}
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();
} }
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;
}
void IDisposable.Dispose() {
foreach (IDisposable element in stack)
element.Dispose();
}
Stack<IDisposable> stack = new Stack<IDisposable>();
}
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) third = accumulator.Add(new Third(accumulator.Add(new Second(first))));
else
third = accumulator.Add(new Third(first));
third.DoSomething();
} }
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!