TidyUp
is a class that allows you to use a using
statement, to ensure that something is done when it goes out of scope. It makes it obvious what the "end" type action is for a "start" type action, puts both actions next to each other and ensures that the end action gets called.
This code has a minor quality issue, in that the indentLevel++
statement could be a long way away from the indentLevel--
statement, and one could get updated without the other.
public class ObjectWriter
{
int indentLevel;
public void WriteRootObject(SomeClass someClass)
{
indentLevel = 1;
WriteObject();
}
void WriteObject(SomeClass someClass)
{
WriteProperties(indentLevel, parent)
indentLevel++;
parent.Children.ForEach(child => WriteObject(child));
// you could imagine that there are a lot more statements here
indentLevel--;
}
}
This can be refactored using the TidyUp class like this.
void WriteObject(SomeClass someClass)
{
WriteProperties(indentLevel, parent)
using (new TidyUp(() => indentLevel++, () => indentLevel--))
{
parent.Children.ForEach(child => WriteObject(child));
// you could imagine that there are a lot more statements here
}
}
This makes the scope of the change to indentLevel
obvious, puts both statements next to each other so that their relationship is obvious, and ensures that indentLevel-- is called. In this case it has the additional benefit that the code is indented in the same way as the output.
This can be further refactored as follows
TidyUp Indent() => new TidyUp(() => indentLevel++, () => indentLevel--)
void WriteObject(SomeClass someClass)
{
WriteProperties(indentLevel, parent)
using (Indent())
{
parent.Children.ForEach(child => WriteObject(child));
// you could imagine that there are a lot more statements here
}
}
This can obviously be used for lots of things besides indentation. We have used it frequently to do things like SavePosition
, SaveColumn
, SaveRow
, HtmlTag
, Wrap
, HeaderAndFooter
etc.
The TidyUp class itself is quite simple, as below.
public class TidyUp : IDisposable
{
readonly Action tidyUp;
public TidyUp(Action tidyUp)
{
Contract.Requires(tidyUp != null);
this.tidyUp = tidyUp;
}
public TidyUp(Action makeMess, Action tidyUp) : this(tidyUp)
{
Contract.Requires(makeMess != null);
makeMess();
}
public void Dispose() => tidyUp();
}